C# in Depth學習筆記-從簡單的資料型別開始
C# 1中定義的產品型別
以定義一個表示產品的型別作為開始,然後進行處理。
其中Product 型別內部封裝了幾個屬性。同時還要建立預定義產品的一個列表。
//程式碼清單1-1 public class Product { string name; public string Name { get { return name; } } decimal price; public decimal Price { get { return price; } } public Product(string name,decimal price) {this.name = name; this.price = price; } public static ArrayList GetSampleProducts() { ArrayList list = new ArrayList(); list.Add(new Product("A", 1.99m)); list.Add(new Product("B", 2.99m)); list.Add(new Product("C", 3.99m)); list.Add(new Product("D", 4.99m)); return list; } }
以上C# 1程式碼存在如下3個侷限:
①ArrayList 沒有提供與其內部內容有關的編譯時資訊。不慎在 GetSampleProducts 建立的列表中新增一個字串是完全有可能的,而編譯器對此沒有任何反應。
②程式碼中為屬性提供了公共的取值方法,這意味著如果新增對應的賦值方法,那麼賦值方法也必須是公共的。
③用於建立屬性和變數的程式碼很複雜——封裝一個字串和一個十進位制數應該是一個十分簡單的任務,不該這麼複雜。
來看看C# 2作了哪些改進。
C# 2中的強型別集合
我們所做的第一組改動,針對上面列出的前兩項,包含C# 2中最重要的改變:泛型。
//程式碼清單1-2 public class Product { string name; public string Name { get { return name; } private set { name = value; } } decimal price; public decimal Price { get { return price; } private set { price = value; } } public Product(string name,decimal price) { Name = name; Price = price; } public static List<Product> GetSampleProducts() { List<Product> list = new List<Product>(); list.Add(new Product("A", 1.99m)); list.Add(new Product("B", 2.99m)); list.Add(new Product("C", 3.99m)); list.Add(new Product("D", 4.99m)); return list; } public override string ToString() { return string.Format("{0}:{1}", name, price); } }
現在屬性擁有了私有的賦值方法(我們在建構函式中使用了這兩個賦值方法)。
並且它能非常“聰明”地猜出 List<Product> 是告知編譯器列表中只能包含 Product 。
試圖將一個不同的型別新增到列表中,會造成編譯時錯誤,並且當你從列表中獲取結果時,也並不需要轉換結果的型別。
C# 2解決了原先的3個問題中的2個。下面展示了展示了C# 3如何解決剩下的那個問題。
C# 3中自動實現的屬性
自動實現的屬性和簡化的初始化,相比Lambda表示式等特性來說,有點微不足道,不過它們可以大大地簡化程式碼。
//程式碼清單1-3 public class Product { public string Name { get; private set; } public decimal Price { get; private set; } Product() { } public static List<Product> GetSampleProducts() { return new List<Product>() { new Product{Name="A", Price=1.99m}, new Product{Name="B", Price=2.99m}, new Product{Name="C", Price=3.99m}, new Product{Name="D", Price=4.99m} }; } public override string ToString() { return string.Format("{0}:{1}", Name, Price); } }
不再有任何程式碼、可見的變數與屬性關聯,而且硬編碼的列表是以一種全然不同的方式構建的。
由於沒有 name 和 price 變數可供訪問,我們必須在類中處處使用屬性,這增強了一致性。
現在有一個私有的無參建構函式,用於新的基於屬性的初始化。(設定這些屬性之前,會對每一項呼叫這個建構函式。)
在本例中,實際上可以完全刪除舊的公共建構函式。但這樣一來,外部程式碼就不能再建立其他的產品例項了。
C# 4中的命名實參
對於C# 4,涉及屬性和建構函式時,我們需要回到原始程式碼。
其中有一個原因是為了讓它不易變:儘管擁有私有賦值方法的型別不能被公共地改變,但如果它也不能被私有地改變,將會顯得更加清晰。
不幸的是,對於只讀屬性,沒有快捷方式,但C# 4允許我們在呼叫建構函式時指定實參的名稱,
如下所示,它為我們提供了和C# 3的初始化程式一樣的清晰度,而且還移除了易變性(mutability)。
//程式碼清單1-4 public class Product { readonly string name; public string Name { get { return name; } } readonly decimal price; public decimal Price { get { return price; } } public Product(string name,decimal price) { this.name = name; this.price = price; } public static List<Product> GetSampleProducts() { return new List<Product>() { new Product(name:"A", price:1.99m), new Product(name:"B", price:2.99m), new Product(name:"C", price:3.99m), new Product(name:"D", price:4.99m) }; } public override string ToString() { return string.Format("{0}:{1}", Name, Price); } }
在這個特定的示例中,該特性的好處不是很明顯,但當方法或建構函式包含多個引數時,它可以使程式碼的含義更加清楚——特別是當引數型別相同,或某個引數為 null 時。
當然,你可以選擇什麼時候使用該特性,只在使程式碼更好理解時才指定引數的名稱。
圖1-1總結了 Product 型別的演變歷程。注意,圖中沒有涉及C# 5。這是因為C# 5主要特性(非同步函式)面向的領域還沒有太多的語言支援。稍後,我們將簡單看一下。
到目前為止,你看到的變化幅度都不大。事實上,泛型的加入( List<Product> 語法)或許是C# 2最重要的一個部分。
但是,我們現在只看到了它的部分用處。雖然沒有什麼讓人心跳加快的東西,但我們只是剛剛開始。下一個任務是以字母順序列印產品列表。