在匿名和元組型別之間選擇

相較於其他類型,選擇適當的類型牽涉到考慮其可用性、效能和取捨。 自 C# 3.0 以來,已有匿名型別可供使用,而泛型 System.Tuple<T1,T2> 型別則是隨著 .NET Framework 4.0 引進。 因此,語言層級支援引進了新的選項,例如 System.ValueTuple<T1,T2> 作為名稱暗示提供具有匿名型別彈性的值型別。 在本文中,您將了解何時適合選擇另一種類型。

可用性和功能

C# 3.0 中引進了匿名型別,並搭配 Language-Integrated Query (LINQ) 運算式。 使用 LINQ 時,開發人員通常會將查詢的結果投影至匿名型別,以保存他們正在使用物件中的一些選取屬性。 請考慮下列範例,以具現化 DateTime 物件的陣列,並逐一查看其投影成具有兩個屬性的匿名型別。

var dates = new[]
{
    DateTime.UtcNow.AddHours(-1),
    DateTime.UtcNow,
    DateTime.UtcNow.AddHours(1),
};

foreach (var anonymous in
             dates.Select(
                 date => new { Formatted = $"{date:MMM dd, yyyy hh:mm zzz}", date.Ticks }))
{
    Console.WriteLine($"Ticks: {anonymous.Ticks}, formatted: {anonymous.Formatted}");
}

匿名型別是使用 new 運算子具現化,且會從宣告推斷屬性名稱和型別。 如果相同組件中有兩個或多個匿名物件初始設定式,指定了順序相同並具有相同名稱和類型的屬性序列,編譯器會將這些物件視為相同類型的執行個體。 這些物件會共用編譯器產生的相同類型資訊。

先前的 C# 程式碼片段會投影具有兩個屬性的匿名型別,非常類似下列編譯器產生的 C# 類別:

internal sealed class f__AnonymousType0
{
    public string Formatted { get; }
    public long Ticks { get; }

    public f__AnonymousType0(string formatted, long ticks)
    {
        Formatted = formatted;
        Ticks = ticks;
    }
}

如需詳細資訊,請參閱匿名型別。 在投影至 LINQ 查詢時,Tuple 有相同的功能,您可以將屬性選取為 Tuple。 這些 Tuple 會流經查詢,就像匿名型別一樣。 現在,請考慮使用 System.Tuple<string, long> 的下列範例。

var dates = new[]
{
    DateTime.UtcNow.AddHours(-1),
    DateTime.UtcNow,
    DateTime.UtcNow.AddHours(1),
};

foreach (var tuple in
            dates.Select(
                date => new Tuple<string, long>($"{date:MMM dd, yyyy hh:mm zzz}", date.Ticks)))
{
    Console.WriteLine($"Ticks: {tuple.Item2}, formatted: {tuple.Item1}");
}

使用 System.Tuple<T1,T2> 時,執行個體會公開編號項目的屬性,例如 Item1Item2。 這些屬性名稱可能讓您更難以了解屬性值的意圖,因為屬性名稱只提供序數。 此外,System.Tuple 型別是參考 class 型別。 不過 System.ValueTuple<T1,T2> 是實值 struct 型別。 下列 C# 程式碼片段會使用 ValueTuple<string, long> 以投影到其中。 如此一來,其會使用常值語法來指派。

var dates = new[]
{
    DateTime.UtcNow.AddHours(-1),
    DateTime.UtcNow,
    DateTime.UtcNow.AddHours(1),
};

foreach (var (formatted, ticks) in
            dates.Select(
                date => (Formatted: $"{date:MMM dd, yyyy at hh:mm zzz}", date.Ticks)))
{
    Console.WriteLine($"Ticks: {ticks}, formatted: {formatted}");
}

如需 Tuple 的詳細資訊,請參閱 Tuple 型別 (C# 參考)Tuple (Visual Basic)

先前的範例在功能上全都相等,不過,其可用性及其基礎實作有些微差異。

取捨

您可能想要在 Tuple 上一律使用 ValueTuple,以及匿名型別,但您應該考慮取捨。 這些 ValueTuple 型別是可變動的,而 Tuple 是唯讀的。 匿名型別可以在運算式樹狀架構中使用,而 Tuple 不行。 下表是一些主要差異的概觀。

主要差異

名稱 存取修飾詞 類型 自訂成員名稱 解構支援 運算式樹狀結構支援
匿名型別 internal class ✔️ ✔️
Tuple public class ✔️
ValueTuple public struct ✔️ ✔️

序列化

選擇型別時,其中一個重要考慮是是否需要序列化。 序列化是將物件的狀態轉換成可保存或傳輸之形式的程序。 如需詳細資訊,請參閱序列化。 當序列化很重要時,建立 classstruct 會優先於匿名型別或 Tuple 型別。

效能

這些型別之間的效能取決於情節。 主要影響牽涉到配置與複製之間的取捨。 在大部分情況下,影響很小。 當發生重大影響時,應該採用度量以協助進行決策。

推論

身為開發人員在 Tuple 和匿名型別之間進行選擇時,有數個需要考慮的因素。 一般而言,如果您未使用運算式樹狀架構,且您熟悉 Tuple 語法,請選擇 ValueTuple,因為其提供具有名稱屬性彈性的數值型別。 如果您正在使用運算式樹狀架構,且您想要命名屬性,請選擇匿名型別。 否則,請使用 Tuple

另請參閱