當我們不要定義復雜的方法,事件,構造函數這樣復雜的類的時候,可以動態的生成一個自定義的數據類型 --> 匿名類型。
1.定義匿名類型
定義一個匿名類型時,需要用到 var 關鍵字和對象初始化語法。
var : 編譯器會在編譯時自動生成新類定義(我們無法在C#代碼中看到類的名稱)。
初始化:它將告訴編譯器為新創建的類型創建私有的后台字段和(只讀的)屬性。
通過傳遞參數構建一個匿名類型,並打印相關信息
private static void BiuldAnonymousType(string make, string color, int currSp) { // 使用傳入參數構建匿名類型 var car = new { Make = make, Color = color, CurrSp = currSp }; // 獲取屬性數據 Console.WriteLine($"{car.Color} 的 {car.Make} 時速{car.CurrSp}"); // 匿名類型包含對System.Object中每個虛方法(virtual)的自定義實現 Console.WriteLine($"ToString={car.ToString()}"); }
調用:也可以使用硬編碼構建匿名類型
public static void Show() { Console.WriteLine("fun with anonymous types"); // 注意 匿名類型也可以使用硬編碼創建 // 構建一個匿名對象表示汽車 var car = new { Make = "honda", Color = "blue", CurrSp = 180 }; // 輸出顏色和車 Console.WriteLine($"我的車是{car.Color}{car.Make}"); // 調用輔助方法通過參數創建匿名類型 BiuldAnonymousType("baoma", "white", 220); }
2.匿名類型的內部表示方式
所有的匿名類型都自動繼承Object,所以我們可以在 car 對象上ToString,GetHashCode,Equals,我們嘗試調用一下:
private static void ReflectOverAnonymousType(object obj) { Console.WriteLine($"對象實例:{obj.GetType().Name}"); Console.WriteLine($"類型: {obj.GetType().Name} 基類: {obj.GetType().BaseType}"); Console.WriteLine($"toString():{obj.ToString()}"); Console.WriteLine($"getHashCode():{obj.GetHashCode()}"); }
調用以及結果:
public static void Show() { Console.WriteLine("fun with anonymous types"); // 構建一個匿名對象表示汽車 var car = new { Make = "honda", Color = "blue", CurrSp = 180 }; ReflectOverAnonymousType(car); }
car對象的類型是:<>f__AnonymousType0`3(你的或許不同),匿名類型名稱由編譯器覺得,我們無從干涉,CIL代碼。
3.方法 ToString() 和 GetHashCode() 的實現
1.ToString()
public override string ToString() { StringBuilder builder = new StringBuilder(); builder.Append("{ Color = "); builder.Append(this.<Color>i_Field); builder.Append(", Make = "); builder.Append(this.<Make>i_Field); builder.Append(", CurrSp = "); builder.Append(this.<CurrSp>i_Field); builder.Append("}"); return builder.ToString(); }
2.GetHashCode()
它使用每個匿名類型的變量計算出散列值作為System.Collections.Generic.EqualityComparer<T>的類型輸入,僅當兩個匿名類型有相同的屬性並且被賦予了相同的值,才會產生相同的散列值。
4.匿名類型的相等語義
Equals()
private static void EqualityTest() { // 構建兩個匿名類型,擁有相同的名稱/值對 var oneCar = new { Make = "honda", Color = "blue", CurrSp = 180 }; var twoCar = new { Make = "honda", Color = "blue", CurrSp = 180 }; // 調用Equals if (oneCar.Equals(twoCar)) { Console.WriteLine("Equals“同一個匿名對象"); } else { Console.WriteLine("Equals“不是 同一個匿名對象"); } // 使用 == 操作符 if (oneCar == twoCar) { Console.WriteLine("==“同一個匿名對象"); } else { Console.WriteLine("==“不是 同一個匿名對象"); } // 比較對象類型 if (oneCar.GetType().Name == twoCar.GetType().Name) { Console.WriteLine("同一個類型"); } else { Console.WriteLine("不同類型"); } ReflectOverAnonymousType(oneCar); ReflectOverAnonymousType(twoCar); }
分析一下這樣的結果:
1.Equals():編譯器重寫Equals()在判斷對象相等時使用了基於值得語義(如:筆記兩個對象的每一個數據成員的值)
2.==操作符:是因為匿名類型沒有重載 相等操作符(==,!=),所以==比較的是引用,而不是內容。
3.GetType():是因為如果我們同一程序集中聲明兩個相同的(屬性相同)匿名類型,編譯器只會生成一個匿名類型的定義。
5.包含匿名類型的匿名類型
var order = new { car = new { Make = "honda", Color = "blue", CurrSp = 180 }, price = 200000 }; ReflectOverAnonymousType(order);
總結:
其實,我們應該謹慎使用匿名類型,尤其在使用LINQ時,永遠不要因為匿名類型的出現而放棄使用強類型的類或結構。
其實,匿名類型本身有許多限制:
- 你並沒有控制匿名類型的名稱
- 匿名類型繼承System.Object
- 匿名類型的字段和屬性總是只讀的
- 匿名類型不支持事件,自定義方法,自定義操作符和自定義重寫
- 匿名類型是隱式封閉的(implicit sealed)
- 匿名類型的實體創建只使用默認構造函數
如果,我們需要快速定義一個實體的形狀,而不需要定義其功能時,可以使用匿名類型。
學無止境,望各位看官多多指教。