匿名型別
匿名類型提供一個便利的方法,將一組唯讀屬性封裝成一個物件,而不需要事先明確定義類型。 類型名稱會由編譯器產生,並且無法在原始程式碼層級使用。 每個屬性的類型會由編譯器推斷。
您可以搭配物件初始設定式使用 new
運算子,來建立匿名型別。 如需物件初始設定式的詳細資訊,請參閱物件和集合初始設定式。
下列範例顯示以兩個名為 Amount
和 Message
的屬性初始化的匿名類型。
var v = new { Amount = 108, Message = "Hello" };
// Rest the mouse pointer over v.Amount and v.Message in the following
// statement to verify that their inferred types are int and string.
Console.WriteLine(v.Amount + v.Message);
您通常可以在查詢運算式的 select
子句中使用匿名型別,以從來源序列中的每個物件傳回屬性子集。 如需查詢的詳細資訊,請參閱 LINQ in C#。
匿名類型包含一個或多個公用唯讀屬性。 其他類型的類別成員 (例如方法或事件) 則無效。 用於初始化屬性的運算式不可以是 null
、匿名函式或指標類型。
最常見的情況是使用其他類型的屬性來初始化匿名類型。 下列範例假設存在一個名為 Product
的類別。 類別 Product
包含 Color
和 Price
屬性,以及您不感興趣的其他屬性:
class Product
{
public string? Color {get;set;}
public decimal Price {get;set;}
public string? Name {get;set;}
public string? Category {get;set;}
public string? Size {get;set;}
}
匿名類型宣告的開頭為 new
關鍵字。 宣告會初始化新類型,只使用 Product
中的兩個屬性。 使用匿名型別會使查詢中傳回較小的資料量。
如果未在匿名類型中指定成員名稱,編譯器會為匿名類型成員指定與用於初始化類型之屬性相同的名稱。 您為以運算式初始化的屬性提供一個名稱,如上述範例所示。 在下列範例中,匿名類型的屬性名稱是 Color
和 Price
。 實體是類型集合中的Product
專案products
:
var productQuery =
from prod in products
select new { prod.Color, prod.Price };
foreach (var v in productQuery)
{
Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
}
提示
您可以使用 .NET 樣式規則 IDE0037 來強制執行推斷或明確成員名稱。
您也可以依另一種型別的物件定義欄位:類別、結構,甚至是另一個匿名型別。 做法是使用保存此物件的變數,就像在下列範例中一樣,使用已具現化的使用者定義型別建立兩個匿名型別。 在這兩種情況下, product
匿名型 shipment
別中的字段,且 shipmentWithBonus
會是包含 Product
其每個字段預設值的類型。 而且 bonus
欄位會是編譯器所建立的匿名型別。
var product = new Product();
var bonus = new { note = "You won!" };
var shipment = new { address = "Nowhere St.", product };
var shipmentWithBonus = new { address = "Somewhere St.", product, bonus };
一般而言,當您使用匿名型別初始化變數時,您可以使用 var 將變數宣告為隱含型別區域變數。 由於只有編譯器可以存取匿名類型的基本名稱,因此無法在變數宣告中指定類型名稱。 如需 var
的詳細資訊,請參閱隱含類型區域變數。
您可以如下列範例所示,合併隱含類型區域變數和隱含類型陣列,以建立匿名類型項目的陣列。
var anonArray = new[] { new { name = "apple", diam = 4 }, new { name = "grape", diam = 1 }};
匿名型別是直接衍生自 object
的 class
型別,並且無法轉換成 object
以外之任何類型的類型。 編譯器提供每種匿名類型的名稱,不過您的應用程式無法存取此名稱。 對 Common Language Runtime 來說,匿名類型與其他任何參考類型並無不同。
如果組件中有兩個或多個匿名物件初始設定式,指定了順序相同並具有相同名稱和類型的屬性序列,編譯器會將這些物件視為相同類型的執行個體。 這些物件會共用編譯器產生的相同類型資訊。
匿名型別會搭配運算式的形式支援非破壞性變化。 這可讓您建立匿名型別的新執行個體,其中一或多個屬性具有新的值:
var apple = new { Item = "apples", Price = 1.35 };
var onSale = apple with { Price = 0.79 };
Console.WriteLine(apple);
Console.WriteLine(onSale);
您無法將欄位、屬性、事件或方法的傳回類型,宣告為具有匿名類型。 同樣地,您無法將方法、屬性、建構函式或索引子的型式參數宣告為具有匿名類型。 若要以方法引數的形式來傳遞匿名類型或含有匿名類型的集合,您可以將參數宣告為 object
類型。 不過,針對匿名型別使用 object
會破壞強型別的目的。 如果您必須在方法界限外儲存或傳遞查詢結果,請考慮使用一般具名結構或類別來取代匿名類型。
由於匿名類型上的 Equals 和 GetHashCode 方法會以屬性的 Equals
和 GetHashCode
方法來定義,相同匿名類型的兩個執行個體僅在其所有屬性都相等時,這兩個執行個體才相等。
注意
匿名型別的存取層級為 internal
,因此在不同組件中定義的兩個匿名型別不是相同類型。
因此,在不同組件中定義時,匿名型別的執行個體不能彼此相等,即使其所有屬性都相等也一樣。
匿名型別會覆寫 ToString 方法,並串連以大括弧括住的每個屬性名稱和 ToString
輸出。
var v = new { Title = "Hello", Age = 24 };
Console.WriteLine(v.ToString()); // "{ Title = Hello, Age = 24 }"