匿名型別是 **C# 編譯器** 的語法糖,CLR (Common Language Runtime) 本身**不感知**匿名型別概念,只見到普通的泛型類別。
var p1 = new { Name = "IORI", Age = 27 };
編譯器產生 (IL 反編譯):
.class private auto ansi sealed '<>f__AnonymousType0`2'<<Name>j__TPar, <Age>j__TPar>
extends [mscorlib]System.Object
{
.field private initonly <Name>j__TPar '<Name>i__Field'
.field private initonly <Age>j__TPar '<Age>i__Field'
.method public hidebysig specialname rtspecialname
instance void .ctor(!0 Name, !1 Age) cil managed { ... }
.method public hidebysig virtual instance bool Equals(object value) { ... }
.method public hidebysig virtual instance int32 GetHashCode() { ... }
.method public hidebysig virtual instance string ToString() { ... }
.property instance !0 Name() { .get instance !0 '<>f__AnonymousType0`2'::get_Name() }
.property instance !1 Age() { .get instance !1 '<>f__AnonymousType0`2'::get_Age() }
}
| 特性 | 說明 |
|---|---|
| 繼承 | 隱含繼承 System.Object |
| 密封 | sealed,不可繼承 |
| 不可變 | 所有屬性 initonly (readonly),建構函式一次設定 |
| 泛型 | 依屬性名稱、型別、順序產生泛型參數 |
| Equality | 逐屬性 EqualityComparer<T>.Default.Equals 比較 |
| GetHashCode | 組合各屬性 HashCode |
| ToString | 格式 { Name = IORI, Age = 27 } |
var p1 = new { Name = "A", Age = 1 };
var p2 = new { Name = "A", Age = 1 };
var p3 = new { Name = "B", Age = 1 };
var p4 = new { Age = 1, Name = "A" }; // 順序不同!
Console.WriteLine(p1.GetType() == p2.GetType()); // True (同名稱、型別、順序)
Console.WriteLine(p1.GetType() == p3.GetType()); // True (值不同不影響型別)
Console.WriteLine(p1.GetType() == p4.GetType()); // False (屬性順序不同 → 不同型別)
internal,**無法跨組件邊界傳遞** (無法作為 public API 參數/回傳值)。record、class 或 struct。| 場景 | 適用性 | 備註 |
|---|---|---|
LINQ 投影 select new { ... } | ✅ 最佳 | 區域變數、立即消費 |
| 區域變數暫存多欄位 | ✅ 適合 | 避免建立 DTO 類別 |
| 方法回傳值 | ❌ 避免 | 回傳 object 需反射,失去型別安全 |
| 非同步流程傳遞 | ❌ 避免 | 跨 await 邊界需捕獲至狀態機,易造成記憶體洩漏 |
| 序列化 (JSON) | ✅ 支援 | Newtonsoft.Json / System.Text.Json 皆可正確序列化 |