屬性分為無參屬性和有參屬性(即索引器)。
屬性相對於字段的優點不僅僅是為了封裝,還可以在讀寫的時候做一些額外操作,緩存某些值或者推遲創建一些內部對象,也適用於以線程安全的方式訪問字段。
話說最基本的屬性就不講了,太平常了。
基本上很多文章都是講屬性的好處的,所以下面就講一下屬性的不足:
屬性不能作為out和ref傳參。屬性實質上是方法而不是字段,因為屬性在編譯後實際上是方法。
屬性可能花較長時執行,而字段訪問則是立即完成。
許多人使用屬性是為了線程同步,這就可能造成線程永遠終止,要線程同步就不要使用屬性。
如果屬性所在類可以被遠程訪問,那麼調用屬性會非常慢。
在上面這些情況下,應該優先使用方法而不是屬性。
並且《CLR via C#》的作者十分不推薦使用屬性,而是使用方法。
匿名類型
先上一段匿名類型的代碼
var man=new {Name="Troy",Age=1};
這段代碼在C#編譯器中會先推斷Name和Age的類型,然後給他們分別創建私有字段,再然後就為這兩個私有字段創建只讀屬性。然後就創建一個構造器初始化私有只讀字段。
並重寫Object的Equals(判斷每個字段是否都想等),GetHashCode(根據每個字段的哈希碼生成哈希碼)和ToString(返回“屬性=值”對的以逗號分隔的列表)。
如果編譯器發現代碼中定義了多個匿名類型,但是這些匿名類型的每個屬性都具有相同的類型和名稱以及順序。那麼實際上它只會創建一個匿名類型定義。
Tuple類型
在System命名空間中,有幾個泛型Tuple(元組)類型,它們全部從Object派生,區別只在於元數的個數。
和匿名對象一樣,Tuple創建好後就不可變了(所有屬性都是只讀)。
匿名對象的屬性調用是通過具體的屬性名,然而Tuple類型只能通過Item1這樣的方式調用。例子如下:
var man = new Tuple<string, int, string>("Troy", 1, "男");//構造器方式 var boy = Tuple.Create("Fuckboy", 3, "男");//靜態工廠方法創造 Console.WriteLine(man.Item1);//Troy Console.WriteLine(man.Item2);//1 Console.WriteLine(man.Item3);//男 //匿名對象方式 var myObj = new { Name = "Troy", Age = 1, Sex = "男" }; Console.WriteLine(myObj.Name);//Troy Console.WriteLine(myObj.Age);//1 Console.WriteLine(myObj.Sex);//男 //另一種組合鍵值對的方式,感覺像在寫js dynamic obj = new System.Dynamic.ExpandoObject(); obj.Name = "Troy"; obj.Age = 1; obj.Sex = "男"; Console.WriteLine(obj.Name);//Troy Console.WriteLine(obj.Age);//1 Console.WriteLine(obj.Sex);//男 Console.Read();
有參屬性(索引器)
C#使用數組風格的語法來公開有參屬性。
CLR本身並不區分有參屬性和無參屬性,對它而言,每個屬性都只是類型中定義的一對方法和一些元數據。而用數組風格的語法來玩有參屬性是C#特有的,編譯之後還是會編譯成IL代碼中的方法。
JIT對屬性的優化
對簡單的get和set訪問其方法,JIT編譯後會將訪問其代碼直接嵌入到調用屬性的方法中,這樣就避免了運行時發出調用所產生的開銷。代價就是編譯後的方法變得更大,可以想象一下調用屬性的方法有很多個,那麼嵌入的重復代碼就有很多。
所以我們應該在屬性訪問器裡寫的代碼應該盡可能少,否則用方法也不錯。如果這樣做了,那麼JIT這樣優化後,生成的本機代碼會變得更小,而且執行更快。