在去年PDC2005上,在發布C#2.0 (C# Whidbey)的同時,微軟也同時展示了它們在C# 3.0上的一些計劃。在提到一系列新的語言特性如語言集成查詢(LINQ)等,Redmond同時也介紹了一個新的特性--匿名類型。本文詳細介紹了匿名類型。
匿名類型定義
C#3.0規范將匿名類型描述為從對象初始化器(object initializer)自動推斷和生成的元組類型。在你能夠充分領會這一定義之前,你需要了解"對象初始化器"的概念,它是匿名類型特性的基礎。
對象初始化器給一個對象的一個或者多個域或者屬性指定值。這就意味著你可以通過一系列諸如{a=10,b=20}這樣的賦值操作指定某個對象的一系列屬性。換句話來說,一個匿名類型是原來不存在的,並且沒有在代碼中明確指定的。
注意,編譯器是在編譯時創建匿名類型而非運行時。
你可以通過ILDASM(IL分解器)來分解獲得:
var p1 = new {Name = "A", Price = 3};
在編譯時刻,編譯器使用對象初始化器推斷的屬性來傳見一個新的匿名類型。因而,新類型將會擁有Name和Price的屬性。Get和Set方法和保存這些屬性的相應的私有變量,會自動的生成。在運行時,此類型的一個實例會被創建,這個實例的屬性將會被設置為對象初始化器中指定的值。
C#內部
你可能很驚奇的發現,你可以只定義一些屬性的名稱以及它們的值,C# 3.0會自動的從它們那裡創建類。這是怎麼做到的呢?檢查一下編譯器的處理吧。
這樣開始一行代碼:
var p1 = new {Name = "A", Price = 3};
當C# 3.0編譯器遇到這樣的一個請求的時候,它將在後台將其轉化成更加清楚的表達,如下:
class __Anonymous1 { private string name ; private int price; public string Name{ get { return name; } set { name = value ; } } public int Price{ get { return price; } set { price= value ; } } } __Anonymous1 p1 = new __Anonymous1(); p1.Name = "A"; pt.Price =3
實例學習
你需要安裝Visual Studio 2005和.NET 2.0,然後你可以從這裡下載到LINQ技術的預覽版本。
如果你安裝了Visual Studio 2005,你可以看到在Visual C#下多了3個和LINQ預覽有關的工程模板:LINQ Console Application, LINQ Windows Application, 和LINQ Library。
你可以這樣創建一個使用匿名類型的工程:
1. 打開Visual Studio 2005編輯器,創建一個新工程,選擇LINQ Console作為工程模板;
2. 將新工程命名為AnonTypes並且點擊OK;
3. 在編輯器裡輸入如下代碼:
// Program.cs using System; using System.Query; using System.Data.DLinq; namespace AnonTypes { class Program { static void Main(string[] args) { var p1 = new {Name = "A", Price = 3}; Console.WriteLine("Name = {0}\nPrice = {1}",p1.Name, p1.Price); Console.ReadLine(); } } }
4. 編譯程序
5. 執行程序,獲得如下結果:
Name = A Price = 3
如果你沒有Visual Studio 2005,你仍然可以通過命令行來編譯你的代碼:
C:\Program Files\LINQ Preview\Bin\Csc.exe /reference:"C:\Program Files\LINQ Preview\Bin\System.Data.DLinq.dll" /reference: System.dll /reference:"C:\Program Files\LINQ Preview\Bin\System.Query.dll" /out:AnonTypes.exe /target:exe Program.cs
盡管你沒有清晰的在代碼裡定義一個類,但是C#編譯器自動做了如下工作:
1. 解析類型
2. 創建一個新的類(擁有name和price屬性)
3. 使用這個類來初始化一個新對象
4. 將傳來的參數指定給對象
深入解析代碼
為了了解編譯器如何創建一個新類的,打開ILDASM(在C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin下)並且選擇最近的編譯程序集,AnonTypes.exe。打開樹狀視圖,你可以看到如圖1所示的視圖:
圖1
如果你仔細看,ILDASM展示了一個匿名類型"<Projection>f__0"是如何被創建的。和類同時被創建的是私有變量_Name和_Price。對這兩個變量的Get和Set方法也同時被創建,他們也擁有屬性Name和Price。
雙擊任何方法或者變量來看的清楚一些,如你點擊Name屬性,你將會看到如下代碼:
.property instance string Name() { .get instance string AnonTypes.Program/ '<Projection>f__0'::get_Name() .set instance void AnonTypes.Program/ '<Projection>f__0'::set_Name(string) } // end of property '<Projection>f__0'::Name
多個匿名類型
如果你創建了多個相似的匿名類型,C#編譯器會聰明的發現這一點,只生成一個類和它的兩個實例,比如你輸入如下代碼:
using System; using System.Query; using System.Data.DLinq; namespace AnonTypes { class Program { static void Main(string[] args) { var p1 = new {Name = "A", Price = 3}; var p2 = new {Name = "A", Price = 3}; Console.WriteLine("Name = {0}\nPrice = {1}",p1.Name, p1.Price); Console.ReadLine(); } } }
當你編譯完並用ILDASM打開的時候,結構如下所示:
圖2
你可以看到,因為聲明是類似的,所以C#只創建了一個匿名類,因而優化了程序。但是如果它們不是很類似的話,就會有兩個匿名類。
稍微修改以上代碼,如下:
new {Name = "A", Price = 3}; var p2 = new {Name = "A"};
圖3
你可以看到,C#創建了兩個類,<Projection>f__0 and <Projection>f__1。
總結
這裡我們可以看到,匿名類型為程序員提供了一種機制,該機制使得你無須清晰的聲明類結構。而且,C# 3.0編譯器非常的智能化,在你擁有多個相似的匿名類型時只創建一個匿名類型從而達到了優化程序的目的。