?
正文
簡介
最近面了幾間公司,每次面試完我都特意把題目記住并寫在云筆記上,用于寫博客文章,也為了給新手們補補基礎。
1.抽象類的介紹,抽象類里的虛函數和抽象函數
參考文檔
- 抽象類、密封類及類成員(C# 編程指南)
-
abstract(C# 參考)
簡介
不能初始化的類被叫做抽象類,它們只提供部分實現,但是另一個類可以繼承它并且能創建它們
的實例。抽象類能夠被用于類,方法,屬性,索引器和事件,使用abstract 在一個類聲明中表示該類傾向要作為其它類的基類,成員被標示成abstract,或被包含進一個抽象類,必須被其派生類實現。 - 一個抽象類可以包含抽象和非抽象方法,當一個類繼承于抽象類,那么這個派生類必須實現所有的,一個抽象方法是一個沒有方法體的方法。
- 通過聲明派生類也為抽象,我們可以避免所有或特定的虛方法的實現,這就是抽象類的部分實現。
- 在C#中,一個抽象類能夠繼承另一個非抽象類,另外,繼承了基類的方法,添加新的抽象和非抽象方法是可行的。
- 一個抽象類也能從一個接口來實現,這種情況,我們必須為所有的方法提供方法體,這些方法是來自接口。
- 我們不能把關鍵字abstract 和 sealed 一起用在C#中,因為一個密封類不能夠被抽象。
-
一個抽象類必須為所有的接口成員提供實現
一個用于實現接口的抽象類可能把接口方法安排到抽象方法上。例如
interface I
{void M(); } abstract class C: I { public abstract void M(); }
抽象類特征
- 抽象類不能被實例化;
- 抽象類可以包含抽象方法和訪問器;
- 不能把抽象類用密封(sealed)來修飾,那就意味著類不能被繼承,這違反抽象類被繼承的原則;
- 一個派生于一個抽象類的非抽象類必須包括所有的繼承來的抽象方法和訪問器的實現;
- 在方法和屬性中使用abstract 關鍵字意味著包含它們的實現
抽象方法特征
- 抽象方法是隱式的虛方法;
- 抽象方法的聲明只能在抽象類中;
- 因為抽象方法聲明只提供一個無實現的方式,沒有方法體;
- 方法體的實現被覆寫方法提供,覆寫方法是一個非抽象類的成員;
- 抽象屬性的行為和抽象方法相像,除了不同的聲明形式。
- 在一個靜態屬性中使用abstract 是一個錯誤。
*一個抽象屬性能夠通過派生類使用 override 實現.
2.虛函數和抽象方法
參考文檔
- C#之虛函數
-
虛方法(virtual)和抽象方法(abstract)的區別
虛方法與非虛方法的最大不同是,虛方法的實現可以由派生類所取代,這種取代是通過方法的重寫實現的。
虛方法的特點
- 虛方法前不允許有static,abstract,或override修飾符;
- 虛方法不能是私有的,因此不能使用private修飾符;
- 我們知道一般函數在編譯時就靜態地編譯到了執行文件中,其相對地址在程序運行期間是不發生變化的,而虛函數在編譯期間是不被靜態編譯的,它的相對地址是不確定的,它會根據運行時期對象實例來動態判斷要調用的函數,
其中那個申明時定義的類叫申明類,那個執行時實例化的類叫實例類。
如:A a =new B(); 其中A是申明類,B是實例類。
- 當調用一個對象的函數時,系統會直接去檢查這個對象申明定義的類,即申明類,看所調用的函數是否為虛函數;
- 如果不是虛函數,那么它就直接執行該函數。而如果是一個虛函數,那么這個時候它就不會立刻執行該函數了,而是開始檢查對象的實例類。
- 在這個實例類里,他會檢查這個實例類的定義中是否有實現該虛函數或者重新實現該虛函數(通過override關鍵字)的方法,如果有,它就不會再找了,而是馬上執行該實例類中實現的虛函數的方法。而如果沒有的話,系統就會不停地往上找實例類的父類,并對父類重復剛才在實例類里的檢查,直到找到第一個重載了該虛函數的父類為止,然后執行該父類里重載后的函數。
虛方法(virtual)和抽象方法(abstract)的區別
- 虛方法必須有實現部分,抽象方法沒有提供實現部分,抽象方法是一種強制派生類覆蓋的方法,否則派生類將不能被實例化。如:
//抽象方法
public abstract class Animal { public abstract void Sleep(); public abstract void Eat(); } //虛方法 public class Animal { public virtual void Sleep(){} public virtual void Eat(){} }
- 抽象方法只能在抽象類中聲明,虛方法不是。其實如果類包含抽象方法,那么該類也是抽象的,也必須聲明為抽象的。如:
public class Animal
{public abstract void Sleep(); public abstract void Eat(); }
編譯器會報錯:
Main.cs(10): 'VSTest.Animal.Sleep()' is abstract but it is contained in nonabstract class 'VSTest.Animal'
Main.cs(11): 'VSTest.Animal.Eat()' is abstract but it is contained in nonabstract class 'VSTest.Animal'
- 抽象方法必須在派生類中重寫,這一點跟接口類似,虛方法不必。
public abstract class Animal
{public abstract void Sleep(); public abstract void Eat(); } public class Cat : Animal { public override void Sleep() { Console.WriteLine( "Cat is sleeping" ); } // we need implement Animal.Eat() here }
編譯器會報錯:Main.cs(14): 'VSTest.Cat' does not implement inherited abstract member 'VSTest.Animal.Eat()'
因為我們沒有實現抽象類中所有抽象方法。
3.靜態類和靜態類成員
參考文檔
- 靜態類和靜態類成員(C# 編程指南)
- C#靜態方法與非靜態方法的比較
(一).C#靜態方法與非靜態方法比較一、C#靜態成員
- 靜態成員屬于類所有,非靜態成員屬于類的實例所有。
- 每創建一個類的實例,都會在內存中為非靜態成員新分配一塊存儲;非靜態成員屬于類所有,為各個類的實例所公用,無論類創建了多少實例;類的靜態成員在內存中只占同一塊區域。
(二).C#靜態方法與非靜態方法比較二、C#靜態方法
- C#靜態方法屬于類所有,類實例化前即可使用。
- 非靜態方法可以訪問類中的任何成員,靜態方法只能訪問類中的靜態成員。
- 因為靜態方法在類實例化前就可以使用,而類中的非靜態變量必須在實例化之后才能分配內存,這樣,C#靜態方法調用時無法判斷非靜態變量使用的內存地址。所以無法使用。而靜態變量的地址對類來說是固定的,故可以使用。
(三).C#靜態方法與非靜態方法比較三、C#靜態方法是一種特殊的成員方法
它不屬于類的某一個具體的實例,而是屬于類本身。所以對靜態方法不需要首先創建一個類的實例,而是采用類名.靜態方法的格式 。
-
static方法是類中的一個成員方法,屬于整個類,即不用創建任何對象也可以直接調用!
static內部只能出現static變量和其他static方法!而且static方法中還不能使用this....等關鍵字..因為它是屬于整個類!
- 靜態方法效率上要比實例化高,靜態方法的缺點是不自動進行銷毀,而實例化的則可以做銷毀。
- 靜態方法和靜態變量創建后始終使用同一塊內存,而使用實例的方式會創建多個內存.
-
C#中的方法有兩種:實例方法,靜態方法.
(四).C#靜態方法與非靜態方法比較四、C#靜態方法中獲取類的名稱
靜態方法中用:
string className =
System.Reflection.MethodBase.
GetCurrentMethod().ReflectedType.FullName;
非靜態方法中還可以用:
string className = this.GetType().FullName;
4.靜態構造函數
定義:靜態構造函數用于初始化任何 靜態 數據,或用于執行僅需執行一次的特定操作。 在創建第一個實例或引用任何靜態成員之前,將自動調用靜態構造函數。
靜態構造函數具有以下特點:
- 靜態構造函數既沒有訪問修飾符,也沒有參數。
- 在創建第一個實例或引用任何靜態成員之前,將自動調用靜態構造函數來初始化類。
- 無法直接調用靜態構造函數。
- 在程序中,用戶無法控制何時執行靜態構造函數。
- 靜態構造函數的典型用途是:當類使用日志文件時,將使用這種構造函數向日志文件中寫入項。
- 靜態構造函數在為非托管代碼創建包裝類時也很有用,此時該構造函數可以調用 LoadLibrary 方法。
- 如果靜態構造函數引發異常,運行時將不會再次調用該構造函數,并且在程序運行所在的應用程序域的生存期內,類型將保持未初始化。
5.接口和抽象類
參考文檔
- C#中抽象類和接口的區別
①.抽象類和接口的區別:
- 類是對對象的抽象,可以把抽象類理解為把類當作對象,抽象成的類叫做抽象類.而接口只是一個行為的規范或規定,微軟的自定義接口總是后帶able字段,證明其是表述一類類“我能做。。。”。抽象類更多的是定義在一系列緊密相關的類間,而接口大多數是關系疏松但都實現某一功能的類中;
- 接口基本上不具備繼承的任何具體特點,它僅僅承諾了能夠調用的方法;
- 一個類一次可以實現若干個接口,但是只能擴展一個父類;
- 接口可以用于支持回調,而繼承并不具備這個特點;
- 抽象類不能被密封;
- 抽象類實現的具體方法默認為虛的,但實現接口的類中的接口方法卻默認為非虛的,當然您也可以聲明為虛的;
- (接口)與非抽象類類似,抽象類也必須為在該類的基類列表中列出的接口的所有成員提供它自己的實現。但是,允許抽象類將接口方法映射到抽象方法上;
- 抽象類實現了oop中的一個原則,把可變的與不可變的分離。抽象類和接口就是定義為不可變的,而把可變的座位子類去實現;
- 好的接口定義應該是具有專一功能性的,而不是多功能的,否則造成接口污染。如果一個類只是實現了這個接口的中一個功能,而不得不去實現接口中的其他方法,就叫接口污染;
-
盡量避免使用繼承來實現組建功能,而是使用黑箱復用,即對象組合。因為繼承的層次增多,造成最直接的后果就是當你調用這個類群中某一類,就必須把他們全部加載到棧中!后果可想而知。(結合堆棧原理理解)。同時,有心的朋友可以留意到微軟在構建一個類時,很多時候用到了對象組合的方法。比如 asp.net中,Page類,有Server Request等屬性,但其實他們都是某個類的對象。使用Page類的這個對象來調用另外的類的方法和屬性,這個是非常基本的一個設計原則;
-
如果抽象類實現接口,則可以把接口中方法映射到抽象類中作為抽象方法而不必實現,而在抽象類的子類中實現接口中方法。
②.抽象類和接口的使用
- 如果預計要創建組件的多個版本,則創建抽象類。抽象類提供簡單的方法來控制組件版本;
- 如果創建的功能將在大范圍的全異對象間使用,則使用接口。如果要設計小而簡練的功能塊,則使用接口;
- 如果要設計大的功能單元,則使用抽象類。如果要在組件的所有實現間提供通用的已實現功能,則使用抽象類;
- 抽象類主要用于關系密切的對象;而接口適合為不相關的類提供通用功能。
以下是我在網上看到的幾個形象比喻。
1.飛機會飛,鳥會飛,他們都繼承了同一個接口“飛”;但是F22屬于飛機抽象類,鴿子屬于鳥抽象類;
2. 就像鐵門木門都是門(抽象類),你想要個門我給不了(不能實例化),但我可以給你個具體的鐵門或木門(多態);而且只能是門,你不能說它是窗(單繼承),一個門可以有鎖(接口)也可以有門鈴(多實現)。門(抽象類)定義了你是什么,接口(鎖)規定了你能做什么(一個接口最好只能做一件事,你不能要求鎖也能發出聲音吧(接口污染))。
6.mvc和webform
- 圖解ASP.NET MVC與WebForm的區別
- 深入比較ASP.NET Webform和ASP.NET MVC兩種開發方式的優缺點
- mvc與三層結構終極區別
- MVC:請求過程
- asp.net MVC處理機制
7.mvc的filter
- [ASP.NET MVC 小牛之路]11 - Filter
- ASP.Net MVC開發基礎學習筆記:四、校驗、AJAX與過濾器點
8.項目中前端用了啥框架,怎么實現,以及如何自定義jquery插件
- jquery開發自定義的插件總結
- 做自定義輪播
學習閉包:
(1).學習Javascript閉包(Closure)
(2).javascript的閉包
(3).圖解閉包