C#匿名類型對(duì)象分析
學(xué)習(xí)C#語(yǔ)言時(shí),經(jīng)常會(huì)遇到C#匿名類型對(duì)象問(wèn)題,這里將介紹C#匿名類型對(duì)象問(wèn)題的解決方法。
C#匿名類型對(duì)象
在很多情況下,我們需要一種能夠臨時(shí)將一批具有一定關(guān)聯(lián)的數(shù)據(jù)存放起來(lái)的對(duì)象;或者在某些情況下,我們對(duì)僅一個(gè)對(duì)象的“形狀”(如屬性的名字和類型等)比較感興趣。例如前面我們提到的Book,當(dāng)它和其他商品放在一起進(jìn)行查詢時(shí),我們可能僅對(duì)其名稱和價(jià)格感興趣,并且希望將這兩種屬性放在另外一個(gè)單獨(dú)的臨時(shí)對(duì)象中以備今后使用。這時(shí),我們關(guān)注的僅僅是這個(gè)臨時(shí)對(duì)象具有Name和Price的屬性感興趣,至于它究竟是什么類型就無(wú)關(guān)緊要了。然而,為了使這樣一個(gè)對(duì)象得以存在,我們不得不為這個(gè)無(wú)關(guān)緊要的類型寫(xiě)上一大堆“樣本代碼”,無(wú)非就是定義一個(gè)如BookAsGood的類,其中無(wú)非也就是形如 m_name和m_price的私有域和名為Name與Price的公共可讀寫(xiě)方法。
而在C# 3.0中,我們無(wú)須為這些無(wú)關(guān)緊要的類型浪費(fèi)時(shí)間。通過(guò)使用“匿名類型”,只要在需要一個(gè)這樣的對(duì)象時(shí)使用沒(méi)有類型名字的new表達(dá)式,并用前面提到的對(duì)象初始化器進(jìn)行初始化即可。如:
- var b1 = new { Name = "The First Sample Book", Price = 88.0f };
- var b2 = new { Price = 25.0f, Name = "The Second Sample Book" };
- var b3 = new { Name = "The Third Sample Book", Price = 35.00f };
- Console.WriteLine(b1.GetType());
- Console.WriteLine(b2.GetType());
- Console.WriteLine(b3.GetType());
首先,前面三行聲明并初始化了三個(gè)具有C#匿名類型對(duì)象,它們都將具有公共可讀寫(xiě)屬性Name和Price。我們可以看到,匿名類型的屬性連類型都省掉了,完全是由編譯器根據(jù)相應(yīng)屬性的初始化表達(dá)式推斷出來(lái)的。這三行稱作“C#匿名類型對(duì)象初始化器”,編譯器在遇到這樣的語(yǔ)句時(shí),首先會(huì)創(chuàng)建一個(gè)具有內(nèi)部名稱的類型(所謂的“匿名”只是源代碼層面上的匿名,在最終編譯得到的元數(shù)據(jù)中還是會(huì)有這樣一個(gè)名字的),這個(gè)類型擁有兩個(gè)可讀寫(xiě)屬性,同時(shí)有兩個(gè)私有域用來(lái)存放屬性值;然后,和對(duì)待對(duì)象初始化器一樣,編譯器產(chǎn)生對(duì)象聲明代碼,并依次為每個(gè)屬性賦值。
上面代碼的最后三行用來(lái)檢驗(yàn)?zāi)涿愋驮谶\(yùn)行時(shí)的類型,如果嘗試編譯并運(yùn)行上述代碼,會(huì)得到類似下面的輸出:
- lover_P.CSharp3Samples.Ex03.Program+<Projection>f__0
- lover_P.CSharp3Samples.Ex03.Program+<Projection>f__1
- lover_P.CSharp3Samples.Ex03.Program+<Projection>f__0
這表明編譯器的確為C#匿名類型對(duì)象創(chuàng)建了實(shí)際的類型,并且該類型在代碼中是不可訪問(wèn)的,因?yàn)轭愋偷拿植环螩#語(yǔ)言命名規(guī)則(其中出現(xiàn)了+、<、>等非法字符)。
另外,我們還發(fā)現(xiàn)一個(gè)有趣的現(xiàn)象,由于b1和b2在初始化的時(shí)候其屬性的順序和推斷出來(lái)的類型完全一致,它們的運(yùn)行時(shí)類型也是一樣的;而b2因?yàn)閷傩猿霈F(xiàn)的順序不同于另外兩個(gè)對(duì)象,因此具有不同的運(yùn)行時(shí)類型。通過(guò)下面的代碼,我們可以驗(yàn)證這一事實(shí):
- // 正確的賦值,b1和b3具有相同的類型
- b1 = b3;
- // 錯(cuò)誤的賦值,b1和b2的類型不同
- b1 = b2;
- //如果嘗試編譯這段代碼,對(duì)于第二個(gè)賦值我們會(huì)得到一條編譯錯(cuò)誤
- Cannot implicitly convert type ’lover_P.CSharp3Samples.Ex03.Program.
- <Projection>f__1’ to ’lover_P.CSharp3Samples.Ex03.Program.
- <Projection>f__0’。
這實(shí)際上是C# 3.0編譯器固有的特性,在同一個(gè)程序集中,編譯器將為屬性出現(xiàn)順序和類型完全相同的C#匿名類型對(duì)象生成唯一的一個(gè)類型。而一旦屬性的出現(xiàn)順序或類型有所不同,編譯器就會(huì)生成不同的類型。另外,在兩個(gè)程序集之中,即使屬性出現(xiàn)的順序和類型一致,編譯器也可能會(huì)生成不同的類型,因此具有C#匿名類型對(duì)象是不能跨程序集訪問(wèn)的。
【編輯推薦】