元組類型 (c # 參考) Tuple types (C# reference)

在 c # 7.0 和更新版本中提供,元組功能提供精簡的語法,將多個資料元素群組在輕量資料結構中。Available in C# 7.0 and later, the tuples feature provides concise syntax to group multiple data elements in a lightweight data structure. 下列範例會示範如何宣告元組變數、將它初始化,以及存取其資料成員:The following example shows how you can declare a tuple variable, initialize it, and access its data members:

(double, int) t1 = (4.5, 3);
Console.WriteLine($"Tuple with elements {t1.Item1} and {t1.Item2}.");
// Output:
// Tuple with elements 4.5 and 3.

(double Sum, int Count) t2 = (4.5, 3);
Console.WriteLine($"Sum of {t2.Count} elements is {t2.Sum}.");
// Output:
// Sum of 3 elements is 4.5.

如上述範例所示,若要定義元組類型,您可以指定其所有資料成員的類型,以及(選擇性地)功能變數名稱As the preceding example shows, to define a tuple type, you specify types of all its data members and, optionally, the field names. 您不能在元組類型中定義方法,但是可以使用 .NET 所提供的方法,如下列範例所示:You cannot define methods in a tuple type, but you can use the methods provided by .NET, as the following example shows:

(double, int) t = (4.5, 3);
Console.WriteLine(t.ToString());
Console.WriteLine($"Hash code of {t} is {t.GetHashCode()}.");
// Output:
// (4.5, 3)
// Hash code of (4.5, 3) is 718460086.

從 c # 7.3 開始,元組類型支援等號比較運算子 ==!=Beginning with C# 7.3, tuple types support equality operators == and !=. 如需詳細資訊,請參閱「元組相等」一節。For more information, see the Tuple equality section.

元組類型為實數值型別;元組元素是公用欄位。Tuple types are value types; tuple elements are public fields. 這可讓元組可變的實值型別。That makes tuples mutable value types.

注意

元組功能需要 System.ValueTuple 類型和相關的泛型型別 (例如, System.ValueTuple<T1,T2>) ,這在 .net Core 和 .NET Framework 4.7 和更新版本中都有提供。The tuples feature requires the System.ValueTuple type and related generic types (for example, System.ValueTuple<T1,T2>), which are available in .NET Core and .NET Framework 4.7 and later. 若要在以 .NET Framework 4.6.2 或更早版本為目標的專案中使用元組,請將 NuGet 套件新增 System.ValueTuple 至專案。To use tuples in a project that targets .NET Framework 4.6.2 or earlier, add the NuGet package System.ValueTuple to the project.

您可以定義具有任意大量元素的元組:You can define tuples with an arbitrary large number of elements:

var t = 
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26);
Console.WriteLine(t.Item26);  // output: 26

元組的使用案例Use cases of tuples

元組最常見的其中一個使用案例是做為方法傳回型別。One of the most common use cases of tuples is as a method return type. 也就是說,您可以將方法結果群組在元組傳回型別中,而不是定義 out 方法參數,如下列範例所示:That is, instead of defining out method parameters, you can group method results in a tuple return type, as the following example shows:

var xs = new[] { 4, 7, 9 };
var limits = FindMinMax(xs);
Console.WriteLine($"Limits of [{string.Join(" ", xs)}] are {limits.min} and {limits.max}");
// Output:
// Limits of [4 7 9] are 4 and 9

var ys = new[] { -9, 0, 67, 100 };
var (minimum, maximum) = FindMinMax(ys);
Console.WriteLine($"Limits of [{string.Join(" ", ys)}] are {minimum} and {maximum}");
// Output:
// Limits of [-9 0 67 100] are -9 and 100

(int min, int max) FindMinMax(int[] input)
{
    if (input is null || input.Length == 0)
    {
        throw new ArgumentException("Cannot find minimum and maximum of a null or empty array.");
    }

    var min = int.MaxValue;
    var max = int.MinValue;
    foreach (var i in input)
    {
        if (i < min)
        {
            min = i;
        }
        if (i > max)
        {
            max = i;
        }
    }
    return (min, max);
}

如上述範例所示,您可以直接使用傳回的元組實例,或在不同的變數中解構它。As the preceding example shows, you can work with the returned tuple instance directly or deconstruct it in separate variables.

您也可以使用元組類型,而不是匿名型別。例如,在 LINQ 查詢中。You can also use tuple types instead of anonymous types; for example, in LINQ queries. 如需詳細資訊,請參閱在匿名和元組類型之間選擇For more information, see Choosing between anonymous and tuple types.

一般而言,您會使用元組來分組鬆散相關的資料元素。Typically, you use tuples to group loosely related data elements. 這通常在私用和內部公用程式方法中很有用。That is usually useful within private and internal utility methods. 在公用 API 的案例中,請考慮定義類別結構類型。In the case of public API, consider defining a class or a structure type.

元組功能變數名稱Tuple field names

您可以在元組初始化運算式中或在元組類型的定義中,明確地指定元組欄位的名稱,如下列範例所示:You can explicitly specify the names of tuple fields either in a tuple initialization expression or in the definition of a tuple type, as the following example shows:

var t = (Sum: 4.5, Count: 3);
Console.WriteLine($"Sum of {t.Count} elements is {t.Sum}.");

(double Sum, int Count) d = (4.5, 3);
Console.WriteLine($"Sum of {d.Count} elements is {d.Sum}.");

從 c # 7.1 開始,如果您未指定功能變數名稱,則可能會從元組初始化運算式中對應變數的名稱推斷,如下列範例所示:Beginning with C# 7.1, if you don't specify a field name, it may be inferred from the name of the corresponding variable in a tuple initialization expression, as the following example shows:

var sum = 4.5;
var count = 3;
var t = (sum, count);
Console.WriteLine($"Sum of {t.count} elements is {t.sum}.");

這就是所謂的「元組投影初始化運算式」。That's known as tuple projection initializers. 在下列情況中,不會將變數的名稱投射到元組功能變數名稱:The name of a variable isn't projected onto a tuple field name in the following cases:

  • 候選名稱是元組類型的成員名稱,例如、 Item3 ToStringRestThe candidate name is a member name of a tuple type, for example, Item3, ToString, or Rest.
  • 候選名稱是另一個元組功能變數名稱的複本,不論是明確或隱含。The candidate name is a duplicate of another tuple field name, either explicit or implicit.

在這些情況下,您可以明確地指定欄位的名稱,或依預設名稱存取欄位。In those cases you either explicitly specify the name of a field or access a field by its default name.

元組欄位的預設名稱是 Item1Item2 等等 Item3The default names of tuple fields are Item1, Item2, Item3 and so on. 您可以一律使用欄位的預設名稱,即使是明確指定或推斷功能變數名稱,如下列範例所示:You can always use the default name of a field, even when a field name is specified explicitly or inferred, as the following example shows:

var a = 1;
var t = (a, b: 2, 3);
Console.WriteLine($"The 1st element is {t.Item1} (same as {t.a}).");
Console.WriteLine($"The 2nd element is {t.Item2} (same as {t.b}).");
Console.WriteLine($"The 3rd element is {t.Item3}.");
// Output:
// The 1st element is 1 (same as 1).
// The 2nd element is 2 (same as 2).
// The 3rd element is 3.

元組指派元組相等比較不會將功能變數名稱納入考慮。Tuple assignment and tuple equality comparisons don't take field names into account.

在編譯時期,編譯器會以對應的預設名稱取代非預設的功能變數名稱。At compile time, the compiler replaces non-default field names with the corresponding default names. 因此,在執行時間無法使用明確指定或推斷的功能變數名稱。As a result, explicitly specified or inferred field names aren't available at run time.

元組指派和解構Tuple assignment and deconstruction

C # 支援在符合下列兩個條件的元組類型之間指派:C# supports assignment between tuple types that satisfy both of the following conditions:

  • 這兩個元組類型具有相同數目的元素both tuple types have the same number of elements
  • 針對每個元組位置,右側元組元素的類型與或隱含轉換成對應的左側元組元素的類型相同。for each tuple position, the type of the right-hand tuple element is the same as or implicitly convertible to the type of the corresponding left-hand tuple element

元組元素值會依照元組元素的順序指派。Tuple element values are assigned following the order of tuple elements. 元組欄位的名稱會被忽略,而且不會指派,如下列範例所示:The names of tuple fields are ignored and not assigned, as the following example shows:

(int, double) t1 = (17, 3.14);
(double First, double Second) t2 = (0.0, 1.0);
t2 = t1;
Console.WriteLine($"{nameof(t2)}: {t2.First} and {t2.Second}");
// Output:
// t2: 17 and 3.14

(double A, double B) t3 = (2.0, 3.0);
t3 = t2;
Console.WriteLine($"{nameof(t3)}: {t3.A} and {t3.B}");
// Output:
// t3: 17 and 3.14

您也可以使用指派運算子 = ,在不同的變數中解構元組實例。You can also use the assignment operator = to deconstruct a tuple instance in separate variables. 您可以透過下列其中一種方式來執行此動作:You can do that in one of the following ways:

  • 在括弧內明確宣告每個變數的類型:Explicitly declare the type of each variable inside parentheses:

    var t = ("post office", 3.6);
    (string destination, double distance) = t;
    Console.WriteLine($"Distance to {destination} is {distance} kilometers.");
    // Output:
    // Distance to post office is 3.6 kilometers.
    
  • 使用 var 括弧外的關鍵字來宣告隱含類型的變數,並讓編譯器推斷其類型:Use the var keyword outside the parentheses to declare implicitly typed variables and let the compiler infer their types:

    var t = ("post office", 3.6);
    var (destination, distance) = t;
    Console.WriteLine($"Distance to {destination} is {distance} kilometers.");
    // Output:
    // Distance to post office is 3.6 kilometers.
    
  • 使用現有的變數:Use existing variables:

    var destination = string.Empty;
    var distance = 0.0;
    
    var t = ("post office", 3.6);
    (destination, distance) = t;
    Console.WriteLine($"Distance to {destination} is {distance} kilometers.");
    // Output:
    // Distance to post office is 3.6 kilometers.
    

如需解構元組和其他類型的詳細資訊,請參閱解構元組和其他類型For more information about deconstruction of tuples and other types, see Deconstructing tuples and other types.

元組相等Tuple equality

從 C# 7.3 開始,Tuple 型別支援 ==!= 運算子。Beginning with C# 7.3, tuple types support the == and != operators. 這些運算子會比較左邊運算元的成員與右運算元的對應成員,遵循元組元素的順序。These operators compare members of the left-hand operand with the corresponding members of the right-hand operand following the order of tuple elements.

(int a, byte b) left = (5, 10);
(long a, int b) right = (5, 10);
Console.WriteLine(left == right);  // output: True
Console.WriteLine(left != right);  // output: False

var t1 = (A: 5, B: 10);
var t2 = (B: 5, A: 10);
Console.WriteLine(t1 == t2);  // output: True
Console.WriteLine(t1 != t2);  // output: False

如上述範例所示, ==!= 作業並不會將元組功能變數名稱納入考慮。As the preceding example shows, the == and != operations don't take into account tuple field names.

滿足下列兩個條件時,兩個元組可比較:Two tuples are comparable when both of the following conditions are satisfied:

  • 這兩個元組都有相同數目的元素。Both tuples have the same number of elements. 例如, t1 != t2 如果 t1t2 具有不同數目的元素,則不會編譯。For example, t1 != t2 doesn't compile if t1 and t2 have different numbers of elements.
  • 針對每個元組位置,左側和右側元組運算元中的對應元素可與 ==!= 運算子比較。For each tuple position, the corresponding elements from the left-hand and right-hand tuple operands are comparable with the == and != operators. 例如,不會 (1, (2, 3)) == ((1, 2), 3) 編譯,因為 1 無法與比較 (1, 2)For example, (1, (2, 3)) == ((1, 2), 3) doesn't compile because 1 is not comparable with (1, 2).

==!= 運算子會以較低的方式比較元組。The == and != operators compare tuples in short-circuiting way. 也就是說,當作業符合一對不相等的元素或達到元組的結尾時,作業就會停止。That is, an operation stops as soon as it meets a pair of non equal elements or reaches the ends of tuples. 不過,在進行任何比較之前,會評估所有的元組元素,如下列範例所示:However, before any comparison, all tuple elements are evaluated, as the following example shows:

Console.WriteLine((Display(1), Display(2)) == (Display(3), Display(4)));

int Display(int s)
{
    Console.WriteLine(s);
    return s;
}
// Output:
// 1
// 2
// 3
// 4
// False

元組當做 out 參數Tuples as out parameters

通常,您會將具有 out 參數的方法重構為傳回元組的方法。Typically, you refactor a method that has out parameters into a method that returns a tuple. 不過,在某些情況下, out 參數可以是元組類型。However, there are cases in which an out parameter can be of a tuple type. 下列範例顯示如何使用元組作為 out 參數:The following example shows how to work with tuples as out parameters:

var limitsLookup = new Dictionary<int, (int Min, int Max)>()
{
    [2] = (4, 10),
    [4] = (10, 20),
    [6] = (0, 23)
};

if (limitsLookup.TryGetValue(4, out (int Min, int Max) limits))
{
    Console.WriteLine($"Found limits: min is {limits.Min}, max is {limits.Max}");
}
// Output:
// Found limits: min is 10, max is 20

元組 vsSystem.TupleTuples vs System.Tuple

以類型支援的 c # 元組 System.ValueTuple ,與以類型代表的元組不同 System.TupleC# tuples, which are backed by System.ValueTuple types, are different from tuples that are represented by System.Tuple types. 主要的差異如下:The main differences are as follows:

  • ValueTuple類型是實數值型別ValueTuple types are value types. Tuple類型是參考型別Tuple types are reference types.
  • ValueTuple類型是可變動的。ValueTuple types are mutable. Tuple類型是不可變的。Tuple types are immutable.
  • 類型的資料成員 ValueTuple 是欄位。Data members of ValueTuple types are fields. 類型的資料成員 Tuple 為屬性。Data members of Tuple types are properties.

C# 語言規格C# language specification

如需詳細資訊,請參閱下列功能建議事項:For more information, see the following feature proposal notes:

另請參閱See also