請根據可用性、效能與取捨,在匿名類型System.Tuple<T1,T2>System.ValueTuple<T1,T2>、 、 之間選擇。
ValueTuple 提供具有匿名型態彈性的值型別。 本文說明何時選擇每種類型。
可用性和功能
使用 Language-Integrated 查詢(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 會流經查詢,就像匿名類型一樣。 現在,請考慮以下使用 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}");
}
欲了解更多元組資訊,請參閱 元組類型(C# 參考) 或 元組 (Visual Basic)。
上述範例在功能上都是相等的,不過,其可用性和基礎實作稍有差異。
取捨
您可能會想總是使用 ValueTuple 而不是 Tuple,並考慮匿名類型,但其中有一些利弊需要您仔細考量。 ValueTuple 類型是可變的,而 Tuple 類型是唯讀的。 匿名型別可用於表達式樹中,而元組則不能。 下表概述一些主要差異。
主要差異
| 名稱 | 存取修飾詞 | 類型 | 自訂成員名稱 | 解構支援 | 表達式樹支援 |
|---|---|---|---|---|---|
| 匿名型別 | internal |
class |
✔️ | ❌ | ✔️ |
| Tuple | public |
class |
❌ | ❌ | ✔️ |
| ValueTuple | public |
struct |
✔️ | ✔️ | ❌ |
序列化
選擇類型時,有一個重要考慮是是否需要串行化。 序列化是將物件狀態轉換為可保存或傳輸的形式的過程。 如需詳細資訊,請參閱 串行化。 當序列化很重要時,建立 class 或 struct 會優於匿名型別或 tuple型別。
績效
這些類型的效能取決於案例。 主要影響涉及配置與複製之間的取捨。 在大部分情況下,影響很小。 當可能發生重大影響時,應該採取測量來通知決策。
結論
身為開發人員,在選擇使用 Tuple 和匿名類型時,需要考慮多個因素。 一般而言,如果您不使用 表達式樹狀結構,且熟悉元組語法,那麼可以選擇 ValueTuple,因為它們提供具名稱屬性彈性的值型別。 如果您正在使用表達式樹狀架構,而且想要命名屬性,請選擇匿名類型。 否則,請使用 Tuple。