連續五篇委派相關的文章後,這篇來點輕鬆的,談談物件和集合初始設定式,以及超好用的自動實作屬性。
自學筆記這系列是我自己學習的一些心得分享,歡迎指教。這系列的分享,會以 C# + 我比較熟的 Net 3.5 環境為主。
另外本系列預計至少會切成【打地基】和【語法應用】兩大部分做分享。打地基的部分,講的是 LINQ 的組成元素,這部分幾乎和 LINQ 無關,反而是 C# 2.0、C# 3.0 的一堆語言特性,例如:型別推斷、擴充方法、泛型、委派等等,不過都會把分享的範圍限制在和 LINQ 應用有直接相關功能。
PS. LINQ 自學筆記幾乎所有範例,都可直接複製到 LINQPad 4 上執行(大多是用 Statements 和 Program 模式)。因為它輕巧好用,功能強大,寫範例很方便,請大家自行到以下網址下載最新的 LINQPad:http://www.LINQpad.net/。
在 C# 1.0/2.0 的版本,我們可以對 .Net 實值型別、字串和陣列做初始化設定:
int i = 99;
string s = "Leo";
string[] ary = new string[] {"Leo", "Alvin", "Rose"};
不過若類別包含多種不多型別的屬性,就無法透過這種方式處理,除非該類別有可接收多參數的建構子。但是這種方式對開發人員來說,屬性一多,很不方便,因為有時候建立執行個體只要初始化兩個屬性,有時候是三個,有時候是四個,而且還有可能同樣要初始化三個屬性,但是這次和下次要給預設值的是不同的屬性。若是用建構子帶預設值,那就要重複寫好多建構子,實在不直覺也不好用。這個問題,直到 C# 3.0 透過物件初始化設定式的語法才得以解決。
物件初始設定式(Object initializers)允許開發人員在建立物件執行個體的同時,對該物件的欄位、屬性指派初始值,而不需透過物件宣告時所定義之建構式。文字表達不是很清楚,看範例程式最易懂!先看在 C# 1.0/2.0 時,如果要給一個只有預設建構子(也就是無參數建構子)的自訂物件之執行個體指派欄位和屬性值,該怎麼做:
public class Person
{
private string name;
private string address;
private int age;
public string Name {get {return name;} set {name = value;}}
public string Address {get {return address;} set {address = value;}}
public int Age {get {return age;} set {age = value;}}
}
void Main()
{
Person p = new Person();
p.Name = "Leo";
p.Address = "Taipei City, Taiwan(R.O.C)";
p.Age = 35;
}
在 C# 3.0 (也就是 VS2008,.Net 3.5 的版本)中,透過物件初始化設定式,上述建立 Person 執行個體的程式碼可以精簡如下:
void Main()
{
Person p = new Person()
{
Name = "Leo",
Address = "Taipei City, Taiwan(R.O.C)",
Age = 35
};
}
事實上,上述兩個寫法,編譯器編譯後的結果是相同的,所以這個語法精簡,其實是透過編譯器代為處理而已。這樣的好處除了精簡程式碼之外(請注意,對於宣告端的來說精簡的程度更高,因為省去寫好多不同參數的建構子),搭配集合初始化設定式,還有一個好處是可以讓複雜物件的初始化變成函數形態的寫法,方便撰寫程式也更容易理解:
public class Order
{
private int orderId;
private string customerName;
private List<Product> orderItems;
public string CustomerName {get {return customerName;} set {customerName = value;}}
public int OrderId {get {return orderId;} set {orderId = value;}}
public List<Product> OrderItems {get {return orderItems;} set {orderItems = value;}}
}
public class Product
{
private int productId;
private string name;
public int ProductId {get {return productId;} set {productId = value;}}
public string Name {get {return name;} set {name = value;}}
}
void Main()
{
Order o = new Order()
{
OrderId = 1314,
CustomerName = "Leo",
OrderItems = new List<Product>
{
new Product()
{
ProductId = 10,
Name = "Egg"
},
new Product()
{
ProductId = 20,
Name = "Apple"
}
}
};
o.Dump();
}
所謂「集合初始化設定式」(Collection initializers)指的就是上述程式碼中,我們新增 OrderItems 這個 List<Product> 的樣子。原本我們要新增一個物件到集合中,通常得利用 Add 的方法:
void Main()
{
Order o = new Order()
{
OrderId = 1314,
CustomerName = "Leo",
OrderItems = new List<Product>()
};
Product p1 = new Product()
{
ProductId = 10,
Name = "Egg"
};
Product p2 = new Product()
{
ProductId = 20,
Name = "Apple"
};
o.OrderItems.Add(p1);
o.OrderItems.Add(p2);
o.Dump();
}
但透過集合初始化設定式,是不是看起來就清爽多了。
最後我們想要把上述的程式碼更精簡,也就是利用 C# 3.0 超級無敵好用的「自動實作屬性」(Auto-Implemented Properties)。它也是一個透過編譯器簡化我們撰寫程式碼的功能。簡化那個部分?就是不用再自行撰寫存放屬性值的私有欄位,編譯器會在編譯時期自動幫我們產生,這樣程式碼就可以大幅精簡,以上例訂單(Order)和產品(Product)類別來看,會變成這樣:
public class Order
{
public string CustomerName {get; set;}
public int OrderId {get; set;}
public List<Product> OrderItems {get; set;}
}
public class Product
{
public int ProductId {get; set;}
public string Name {get; set;}
}
很棒對喔!自動實作屬性和 LINQ 構成元素無關,但是因為太好用了,所以一定要帶到,這樣後續文章要寫範例程式碼就可以輕鬆多了。
PS. 自動實作屬性,VB 要到 .Net 4.0 才提供。