弱小和無知不是生存的障礙,傲慢纔是!——《三體》css
1. const和readonly有什麼區別?html
2. 哪些類型能夠定義爲常量?常量const有什麼風險?面試
3. 字段與屬性有什麼異同?編程
4. 靜態成員和非靜態成員的區別?安全
5. 自動屬性有什麼風險?閉包
6. 特性是什麼?如何使用?異步
7. 下面的代碼輸出什麼結果?爲何?ide
List<Action> acs = new List<Action>(5); for (int i = 0; i < 5; i++) { acs.Add(() => { Console.WriteLine(i); }); } acs.ForEach(ac => ac());
8. C#中的委託是什麼?事件是否是一種委託?函數式編程
常量的基本概念就不細說了,關於常量的幾個特色總結一下:函數
關於常量不支持跨程序集版本更新,舉個簡單的例子來講明:
public class A { public const int PORT = 10086; public virtual void Print() { Console.WriteLine(A.PORT); } }
上面一段很是簡單代碼,其生產的IL代碼以下,在使用常量變量的地方,把她的值拷過來了(把常量的值內聯到使用的地方),與常量變量A.PORT沒有關係了。假如A引用了B程序集(B.dll文件)中的一個常量,若是後面單獨修改B程序集中的常量值,只是從新編譯了B,而沒有編譯程序集A,就會出問題了,就是上面所說的不支持跨程序集版本更新。常量值更新後,全部使用該常量的代碼都必須從新編譯,這是咱們在使用常量時必需要注意的一個問題。
接着上面的const說,其實枚舉enum也有相似的問題,其根源和const同樣,看看代碼你就明白了。下面的是一個簡單的枚舉定義,她的IL代碼定義和const定義是同樣同樣的啊!枚舉的成員定義和常量定義同樣,所以枚舉其實本質上就至關是一個常量集合。
public enum EnumType : int { None=0, Int=1, String=2, }
字段自己沒什麼好說的,這裏說一個字段的內聯初始化問題吧,可能容易被忽視的一個小問題(不過好像也沒什麼影響),先看看一個簡單的例子:
public class SomeType { private int Age = 0; private DateTime StartTime = DateTime.Now; private string Name = "三體"; }
定義字段並初始化值,是一種很常見的代碼編寫習慣。但注意了,看看IL代碼結構,一行代碼(定義字段+賦值)被拆成了兩塊,最終的賦值都在構造函數裏執行的。
那麼問題來了,若是有多個構造函數,就像下面這樣,有多半個構造函數,會形成在兩個構造函數.ctor中重複產生對字段賦值的IL代碼,這就形成了沒必要要的代碼膨脹。這個其實也很好解決,在非默認構造函數後加一個「:this()」就OK了,或者顯示的在構造函數裏初始化字段。
public class SomeType { private DateTime StartTime = DateTime.Now; public SomeType() { } public SomeType(string name) { } }
屬性是面向對象編程的基本概念,提供了對私有字段的訪問封裝,在C#中以get和set訪問器方法實現對可讀可寫屬性的操做,提供了安全和靈活的數據訪問封裝。咱們看看屬性的本質,主要手段仍是IL代碼:
public class SomeType { public int Index { get; set; } public SomeType() { } }
上面定義的屬性Index被分紅了三個部分:
所以能夠說屬性的本質仍是方法,使用面向對象的思想把字段封裝了一下。在定義屬性時,咱們能夠自定義一個私有字段,也可使用自動屬性「{ get; set; } 」的簡化語法形式。
使用自動屬性時須要注意一點的是,私有字段是由編譯器自動命名的,是不受開發人員控制的。正由於這個問題,曾經在項目開發中遇到一個所以而產生的Bug:
這個Bug是關於序列化的,有一個類,定義不少個(自動)屬性,這個類的信息須要持久化到本地文件,當時使用了.NET自帶的二進制序列化組件。後來由於一個需求變動,把其中一個字段修改了一下,須要把自動屬性改成本身命名的私有字段的屬性,就像下面實例這樣。測試序列化到本地沒有問題,反序列化也沒問題,但最終bug仍是被測試出來了,問題在與反序列化之前(修改代碼以前)的本地文件時,Index屬性的值丟失了!!!
private int _Index; public int Index { get { return _Index; } set { _Index = value; } }
由於屬性的本質是方法+字段,真正的值是存儲在字段上的,字段的名稱變了,反序列化之前的文件時找不到對應字段了,致使值的丟失!這也就是使用自動屬性可能存在的風險。
什麼是委託?簡單來講,委託相似於 C或 C++中的函數指針,容許將方法做爲參數進行傳遞。
.NET中沒有函數指針,方法也不可能傳遞,委託之所能夠像一個普通引用類型同樣傳遞,那是由於她本質上就是一個類。下面代碼是一個很是簡單的自定義委託:
public delegate void ShowMessageHandler(string mes);
看看她生產的IL代碼
咱們一行定義一個委託的代碼,編譯器自動生成了一堆代碼:
所以,也就不難猜想,當咱們調用委託的時候,其實就是調用委託對象的Invoke方法,能夠驗證一下,下面的調用代碼會被編譯爲對委託對象的Invoke方法調用:
private ShowMessageHandler ShowMessage; //調用 this.ShowMessage("123");
閉包提供了一種相似腳本語言函數式編程的便捷、能夠共享數據,但也存在一些隱患。
題目列表中的第7題,就是一個.NET的閉包的問題。
List<Action> acs = new List<Action>(5); for (int i = 0; i < 5; i++) { acs.Add(() => { Console.WriteLine(i); }); } acs.ForEach(ac => ac()); // 輸出了 5 5 5 5 5,全是5?這必定不是你想要的吧!這是爲何呢?
上面的代碼中的Action就是.NET爲咱們定義好的一個無參數無返回值的委託,從上一節咱們知道委託實質是一個類,理解這一點是解決本題的關鍵。在這個地方委託方法共享使用了一個局部變量i,那生成的類會是什麼樣的呢?看看IL代碼:
共享的局部變量被提高爲委託類的一個字段了:
那該如何修正呢?很簡單,委託方法使用一個臨時局部變量就OK了,不共享數據:
List<Action> acss = new List<Action>(5); for (int i = 0; i < 5; i++) { int m = i; acss.Add(() => { Console.WriteLine(m); }); } acss.ForEach(ac => ac()); // 輸出了 0 1 2 3 4
至於原理,能夠本身探索了!
const關鍵字用來聲明編譯時常量,readonly用來聲明運行時常量。均可以標識一個常量,主要有如下區別:
一、初始化位置不一樣。const必須在聲明的同時賦值;readonly便可以在聲明處賦值,也能夠在構造方法裏賦值。
二、修飾對象不一樣。const便可以修飾類的字段,也能夠修飾局部變量;readonly只能修飾類的字段 。
三、const是編譯時常量,在編譯時肯定該值,且值在編譯時被內聯到代碼中;readonly是運行時常量,在運行時肯定該值。
四、const默認是靜態的;而readonly若是設置成靜態須要顯示聲明 。
五、支持的類型時不一樣,const只能修飾基元類型或值爲null的其餘引用類型;readonly能夠是任何類型。
基元類型或值爲null的其餘引用類型,常量的風險就是不支持跨程序集版本更新,常量值更新後,全部使用該常量的代碼都必須從新編譯。
由於自動屬性的私有字段是由編譯器命名的,後期不宜隨意修改,好比在序列化中會致使字段值丟失。
特性與屬性是徹底不相同的兩個概念,只是在名稱上比較相近。Attribute特性就是關聯了一個目標對象的一段配置信息,本質上是一個類,其爲目標元素提供關聯附加信息,這段附加信息存儲在dll內的元數據,它自己沒什麼意義。運行期以反射的方式來獲取附加信息。使用方法能夠參考:http://www.cnblogs.com/anding/p/5129178.html
List<Action> acs = new List<Action>(5); for (int i = 0; i < 5; i++) { acs.Add(() => { Console.WriteLine(i); }); } acs.ForEach(ac => ac());
輸出了 5 5 5 5 5,全是5!由於閉包中的共享變量i會被提高爲委託對象的公共字段,生命週期延長了
什麼是委託?簡單來講,委託相似於 C或 C++中的函數指針,容許將方法做爲參數進行傳遞。
事件能夠理解爲一種特殊的委託,事件內部是基於委託來實現的。
版權全部,文章來源:http://www.cnblogs.com/anding
我的能力有限,本文內容僅供學習、探討,歡迎指正、交流。
.NET面試題解析(00)-開篇來談談面試 & 系列文章索引
書籍:CLR via C#
書籍:你必須知道的.NET