今天的篇幅應該會很長,除了回顧前面學的一些,還有寫一些關於匿名類型的相關知識,總體上對後續的學習很有幫助,學好了,後面更容易理解,不明白的,那就前面多翻幾次,看多了總是會理解的。那麼,進入正題吧。
我們的很多工作都是由編譯器幫我們去完成,如我們要說的自動實現屬性。使用自動實現屬性時,C#3執行了一個簡單的編譯轉換,在類的內部生成一個私有的字段,使用不友好的命名(防止命名沖突)。在C#2中允許為取值和賦值方法指定不同的訪問權限,現在我們還可以創建靜態的自動屬性。
使用隱式類型,在編寫代碼時,沒有顯式地聲明類型,但在編譯的結果中,編譯器會獲取初始化表達式的類型,使用變量也具有該類型。隱式類型只有在以下幾種情況才能使用:
一種方法是使用無參的構造函數先實例化一個對象,然後分別為每個公開的屬性賦值。另一種則是在構造函數中將屬性值作為參數,在構造函數中為每個屬性賦值,這裡可以給公開和私有的賦值。使用自動實現屬性,則可以使用對象初始化器,如下,三個類Computer、Mouse、User
1 class Computer 2 { 3 public string Cpu { get; set; } 4 public Mouse Mouse { get; set; } 5 public List<User> Users { get; set; } 6 } 7 class Mouse 8 { 9 public string Brand { get; set; } 10 } 11 class User 12 { 13 public string Name { get; set; } 14 }
上述三個類都沒有構造函數(有一個默認的無參構造函數),使用對象初始化器就能很方便的去實例化對象。
1 Computer c = new Computer() 2 { 3 Cpu = "AMD", 4 Mouse = new Mouse() { Brand = "羅技" }, 5 Users = new List<User> { 6 new User() {Name="小A" }, 7 new User() {Name="小B" }, 8 new User() {Name="小C" } 9 } 10 };
可以看到上述的每一個對象實例都使用了對象初始器來實例化對象,調用的構造函數都是系統默認的構造函數,當我們將無參的構造函數設置為私有時,上述語句將無效。那我們就可以想像,當有一個為Cup賦值的構造函數,則在new Computer(cupName)接大括號"{}"來初始化對象,如
1 class Computer 2 { 3 public string Cpu { get; set; } 4 public Mouse Mouse { get; set; } 5 public List<User> Users { get; set; } 6 public Computer(string cpu) { 7 this.Cpu = cpu; 8 } 9 public Computer() 10 { 11 12 } 13 } 14 15 Computer c1 = new Computer("AMD") 16 { 17 Mouse = new Mouse() { Brand = "羅技" }, 18 Users = new List<User> { 19 new User() {Name="小A" }, 20 new User() {Name="小B" }, 21 new User() {Name="小C" } 22 } 23 };
注:調用無參的構造函數時,使用省略類名後面的括號。
是不是看上面的代碼不斷是很多,那我們可以再精簡一下new List<User>,和new Mouse。
1 Computer c1 = new Computer("AMD") 2 { 3 Mouse = { Brand = "羅技" }, 4 Users = { 5 new User() {Name="小A" }, 6 new User() {Name="小B" }, 7 new User() {Name="小C" } 8 } 9 };
我們直接將類型名稱給去除了,看到這裡是否也想到可以把new User也給去除了,不過我試過是不行的,應該是無法確定要轉換的類型吧。這個有點類似在C++11中統一使用"{}"來初始化對象。關於集合初始化可以參照上述中的對屬性Users的初始化,而上述中Mouse = { Brand = "羅技" }則稱呼為初始化嵌入對象。
在C#1和C#2中,數組的聲明和初始化如下
1 string[] names = { "a", "b", "c" };
如果一個方法的簽名如下:
1 public void Method0(string[] names)
那使用大括號中的表達式不能作為參數傳入該方法,如
1 Method0({ "a", "b", "c" });
必須要告訴編譯器傳入的數組是什麼類型的數組,如
1 Method0(new string[] { "a", "b", "c" });
但如果我們顯式的指定類型,可以讓編譯器自己推斷,則用到了隱式類型數組,裡面也有涉及到協變性,如果A繼承於B,參數為A的數組,那麼我們使用隱式類型的數組,就可以傳入A實例和B實例的數組。如:
1 class A 2 { 3 } 4 5 class B : A 6 { 7 } 8 9 public static void Method1(A[] args) 10 { 11 12 } 13 14 A a = new A(); 15 B b = new B(); 16 Method1(new[] { a, b }); 17 Method1(new[] { new A(), new B() });
匿名類型常用作用於LINQ中返回一系列的沒有具體類型名的對象,也可以使用單獨使用(在不想創建多余的類時)。
1 var p = new { Name = "a", Age = 12 };
接下來,就可以使用變量p,p有兩個屬性Name="a"和Age=12,也可以使用匿名類型來初始化數組,如
1 var ps = new[] { 2 new { Name = "a", Age = 12}, 3 new { Name = "a", Age = 12}, 4 new { Name = "a", Age = 12} 5 };
匿名類型包含以下成員:
關於投影初始化程序,簡單地理解從一個集合中,抽取集合元素中的各別屬性,組成一個匿名類型,從而返回一個包含匿名類型的集合,這也是為什麼我們使用var關鍵字,因為我們真的不知道返回的類型,使用var讓編譯器替我們理解返回類型,那問題來了,這個匿名類型的聲明是否由編譯器幫我們生成?它幫我們生成了類型,使用反編譯工具就能知曉。
請斧正。