튜플 형식(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.

참고

튜플 기능을 사용하려면 .NET Core 및 .NET Framework 4.7 이상에서 사용할 수 있는 System.ValueTuple 형식 및 관련 제네릭 형식(예: System.ValueTuple<T1,T2>)이 필요합니다.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, ToString 또는 Rest)The 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.

튜플 필드의 기본 이름은 Item1, Item2, Item3 등입니다.The 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부터 튜플 형식에서는 ==!= 연산자를 지원합니다.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. 예를 들어 t1t2의 요소 수가 다른 경우 t1 != t2는 컴파일되지 않습니다.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(1, 2)와 비교할 수 없기 때문에 (1, (2, 3)) == ((1, 2), 3)은 컴파일되지 않습니다.For example, (1, (2, 3)) == ((1, 2), 3) doesn't compile because 1 is not comparable with (1, 2).

==!= 연산자는 단락(short-circuiting) 방식으로 튜플을 비교합니다.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

출력 매개 변수 튜플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

튜플과 System.Tuple 비교Tuples vs System.Tuple

System.ValueTuple 형식으로 지원되는 C# 튜플은 System.Tuple 형식으로 표현되는 튜플과 다릅니다.C# 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