在去年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編 譯器非常的智能化,在你擁有多個相似的匿名類型時只創建一個匿名類型從而達 到了優化程序的目的。