在匿名和元組型別之間選擇
相較於其他類型,選擇適當的類型牽涉到考慮其可用性、效能和取捨。 自 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> 時,執行個體會公開編號項目的屬性,例如 Item1
和 Item2
。 這些屬性名稱可能讓您更難以了解屬性值的意圖,因為屬性名稱只提供序數。 此外,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 |
✔️ | ✔️ | ❌ |
序列化
選擇型別時,其中一個重要考慮是是否需要序列化。 序列化是將物件的狀態轉換成可保存或傳輸之形式的程序。 如需詳細資訊,請參閱序列化。 當序列化很重要時,建立 class
或 struct
會優先於匿名型別或 Tuple 型別。
效能
這些型別之間的效能取決於情節。 主要影響牽涉到配置與複製之間的取捨。 在大部分情況下,影響很小。 當發生重大影響時,應該採用度量以協助進行決策。
推論
身為開發人員在 Tuple 和匿名型別之間進行選擇時,有數個需要考慮的因素。 一般而言,如果您未使用運算式樹狀架構,且您熟悉 Tuple 語法,請選擇 ValueTuple,因為其提供具有名稱屬性彈性的數值型別。 如果您正在使用運算式樹狀架構,且您想要命名屬性,請選擇匿名型別。 否則,請使用 Tuple。