튜플(Tuple)

  • 12 minutes to read

메서드의 매개 변수를 전달할 때 사용자 정의 클래스를 사용하면 한 번에 여러 개의 값을 전달할 수 있습니다. 튜플은 새로운 클래스를 만들지 않고도 언어 차원에서 여러 개의 값을 전달할 수 있는 기능을 제공하여 편리함을 줍니다. 이번 강의는 C# 7.0부터 제공되는 튜플의 편리함을 학습해 보도록 하겠습니다.

> // 튜플: 괄호 기호의 간단한 구문을 사용하여 하나 이상의 속성을 가지는 개체를 만드는 형식

튜플(Tuple)

C# 7.0 버전부터 제공하는 튜플(Tuple)은 한번에 하나 이상의 값을 전달하거나 제공받을 때 사용되는 데이터 구조입니다. 튜플은 프로그래밍 자체에서 따로 추가 클래스를 사용하지 않고 괄호를 사용하여 하나 이상의 값을 그룹화하여 사용합니다.

다음 코드 조각을 살펴보면 괄호를 사용하여 한번에 2개의 정수 값을 xy의 이름으로 반환합니다.

> (int x, int y) GetPoint()
. {
.     return (10, 20);
. }
> GetPoint().x
10
> GetPoint().y
20

튜플은 함수에서 여러 값을 반환하는 데 도움이 많이 됩니다. 함수에서 여러 값을 반환하는 방법은 많이 있지만 C# 7.0 이후에 새로 도입된 튜플만큼 편리하지는 않습니다.

튜플 형식은 눈에 띄는 몇 가지 장점이 있습니다.

  • 명시적인 형식을 지정할 필요가 없습니다. 컴파일러의 형식을 유추해 자동으로 형식을 제공합니다.
  • 기존에 사용하던 Tuple 클래스는 Item1, Item2, ... 형태로 접근했지만, 튜플을 사용하면 원하는 이름의 속성을 만들 수 있습니다.

이 내용은 이어지는 각 예제를 통해 좀 더 자세히 살펴보겠습니다.

튜플 리터럴

변수에 괄호를 사용하여 하나 이상의 값을 설정하는 것을 튜플 리터럴(Tuple Literal)이라고 합니다.

코드: TupleLiteralDescription.cs

> //[?] 튜플 리터럴(Tuple Literal)
> var r = (12, 34, 56); // 3개의 int 형식 데이터가 r 변수에 담김
> $"{r.Item1}, {r.Item2}, {r.Item3}"
"12, 34, 56"

괄호를 사용하여 하나 이상의 값을 변수에 선언하면 Item1, Item2, Item3 형태로 값이 저장됩니다.

튜플 리터럴의 여러 가지 사용법

튜플 리터럴의 여러 가지 사용법을 정리해보겠습니다.

코드: TupleLiteral.cs

> var fhd = (1920, 1080); //[1] 기본: Item1, Item2 형태 
> $"Full HD: {fhd.Item1} * {fhd.Item2}"
"Full HD: 1920 * 1080"
> 
> var uhd = (Width: 3840, Height: 2160); //[2] 이름 지정
> $"4K UHD: {uhd.Width} * {uhd.Height}"
"4K UHD: 3840 * 2160"
> 
> (ushort Width, ushort Height) hd = (1366, 768); //[3] 이름과 형식 지정
> $"HD: {hd.Width} * {hd.Height}"
"HD: 1366 * 768"
> $"Type({hd.Width.GetType()}, {hd.Height.GetType()})"
"Type(System.UInt16, System.UInt16)"

[1]번 코드가 튜플의 기본 사용법이라면 [2]번 코드처럼 새로운 속성 이름을 지정할 수 있습니다. 마찬가지로 var 대신에 (int a, int b) 형태로 변수를 사용하여 이름과 형식을 한꺼번에 지정할 수도 있습니다.

튜플(Tuple) 반환

한번에 하나 이상을 반환시켜주는 기능을 튜플 반환이라고 합니다. 튜플 반환에는 튜플 리터럴이 사용됩니다.

코드: TupleReturnDescription.cs

> //[1] 튜플 리턴(Tuple Return) 형식: (int, int) 
> static (int, int) Tally1()
. {
.     var r = (12, 3); //[A] 튜플 리터럴에 2개의 값 담기
.     return r; //[B] 튜플 리터럴 반환
. }
> 
> //[2] 튜플 리턴에 이름 값 지정 가능
> static (int Sum, int Count) Tally2() => (45, 6); 
> 
> var t1 = Tally1();
> $"Sum: {t1.Item1}, Count: {t1.Item2}"
"Sum: 12, Count: 3"
> 
> var t2 = Tally2();
> $"Sum: {t2.Sum}, Count: {t2.Count}"
"Sum: 45, Count: 6"

[1]번 코드의 (int, int) 형태와 [2]번 코드의 (int Sum, int Count) 형태로 하나 이상의 반환값을 지정할 수 있습니다. 기존에는 int, string과 같은 기본 형식이 아닌 경우에는 새롭게 모델 클래스를 만들고 이의 속성을 통해서 하나 이상의 결과를 반환했는데요. 튜플을 사용하면 괄호 기호 안에 하나 이상의 결과를 지정하여 반환할 수 있습니다. 튜플 반환에 필요한 결과를 담을 그릇은 튜플 리터럴이 사용됩니다.

이름이 지정된 튜플

튜플 리터럴에는 이름을 지정하여 Item1, Item2 형태보다는 좀 더 의미있는 이름을 사용할 수 있습니다.

코드: TupleNamed.cs

> var boy = (Name: "철수", IsStudent: true, OrderPrice: 1_000 );
> $"{boy.Name}(초등학생: {boy.IsStudent}) - 주문: {boy.OrderPrice:C0}"
"철수(초등학생: True) - 주문: ₩1,000"

튜플 반환에 기본값 설정

이번에는 default 키워드를 사용하여 튜플의 기본 반환값을 설정하는 방법을 알아보겠습니다.

코드: TupleDefault.cs

> static (int, int) ZeroZero() => default;
> var t = ZeroZero();
> $"{t.Item1}, {t.Item2}"
"0, 0"

이 코드 형태는 중요한 것이 아니니 저런 모양도 있구나 하고 넘어가면 됩니다.

튜플 반환값에 이름 지정하기

이번에는 튜플의 반환값에 의미있는 이름을 지정하는 방법을 알아보겠습니다.

코드: TupleReturnName.cs

> // 튜플 반환값에 first와 second 이름 지정하기 
> static (int first, int second) NameTuple()
. {
.     var r = (100, 200);
.     return r;
. }
> 
> var t = NameTuple();
> $"{t.first}, {t.second}"
"100, 200"

튜플의 반환 형식에 단순히 int와 같은 형식만을 지정하는게 아닌 (int first, int second)로 이름을 지정할 수 있습니다. 이렇게 이름이 지정된 튜플은 호출하는 쪽에서 t.first 형태로 속성 모양으로 받아 사용할 수 있습니다.

튜플 분해(Deconstructing)

튜플 반환값을 원하는 변수로 받아서 사용하는 형태를 튜플 분해(Deconstructing) 또는 튜플 해체라고 합니다.

코드: TupleDeconstructionDescription.cs

using System;
using static System.Console;

class TupleDeconstructionDescription
{
    static void Main()
    {
        // 튜플 분해(Tuple Deconstruction) 또는 튜플 해체 작업
        var (sum, count) = Tally();
        WriteLine($"Sum: {sum}, Count: {count}");
    }

    static (int Sum, int Count) Tally()
    {
        var r = (s: 12, c: 3);

        Console.WriteLine($"{r.s}, {r.c}");

        return r;
    }
}
12, 3
Sum: 12, Count: 3

Tally() 함수의 결과는 튜플로 넘어오는데 이를 var (sum, count) = Tally(); 형태로 받으면 sum 변수에는 Sum 반환값이 저장되고 count 변수에는 Count 반환값이 저장되는 형태입니다.

로컬 함수와 튜플 함께 사용하기

로컬 함수는 무겁지 않은 간단한 기능을 사용할 때 유용한데요. 이러한 로컬 함수와 튜플을 함께사용하는 예제를 만들어 보겠습니다.

코드: LocalFunctionDescription.cs

using static System.Console;

class LocalFunctionDescription
{
    static void Main()
    {
        // [1] numbers = { 1, 2, 4, 8 }
        int[] numbers = { 0b1, 0B10, 0b0100, 0B0000_1000 };
        var (sum, count) = Tally(numbers);
        WriteLine($"Sum: {sum}, Count: {count}");
    }

    static (int, int) Tally(int[] values)
    {
        var r = (Sum: 0, Count: 0);
        foreach (var v in values)
        {
            Add(v, 1);
        }
        return r;

        // [2] 로컬 함수(Local Function): 함수내에서 또 다른 함수 정의
        void Add(int s, int c)
        {
            r.Sum += s; // 합계
            r.Count += c; // 건수
        }
    }
}
Sum: 15, Count: 4

[1]번 코드와 같이 numbers 배열에는 1, 2, 4, 84건의 데이터가 있고 이의 전체 합계는 15인 배열을 매개변수로 받아서 [2]번 코드 영역의 로컬 함수에서 합계와 건수를 계산 후 이를 튜플 리터럴로 반환해서 다시 Main 메서드에서 출력하는 예제입니다.

참고: C# 7.0 이전의 Tuple<T> 클래스

참고로, C# 7.0 이전에는 언어 차원에서 튜플을 지원하지 않아 Tuple<T> 클래스를 사용하기도 했습니다. Tuple 클래스를 사용하면 하나의 변수에 여러 데이터를 한번에 저장할 수 있는 편리함을 제공합니다.

코드: TupleClass.cs

> var tp = new Tuple<string, bool, decimal>("철수", true, 1_000);
> $"{tp.Item1}(초등학생: {tp.Item2})가 {tp.Item3:C0}원짜리 빵을 샀다."
"철수(초등학생: True)가 ₩1,000원짜리 빵을 샀다."

Tuple<T> 클래스를 사용하면 Tuple<string, bool, decimal> 형태로 하나 이상의 데이터 형식과 같을 저장할 수 있는 개체가 만들어집니다. 각각의 개체는 대입된 순서에 따라 Item1, Item2, … 형태의 속성을 통해서 값에 접근할 수 있습니다.

Tuple<T> 클래스 사용 예제를 하나 더 사용해보겠습니다.

코드: TupleClassDemo.cs

> //[1] 생성자를 통한 튜플 개체 생성
> var one = new Tuple<int>(1234);
> var two = new Tuple<int, string>(1, "C#");
> var many = new Tuple<int, string, string>(2025, "C#", "13.0");
> 
> //[2] Tuple.Create 메서드를 통한 튜플 개체 생성
> var tuple = Tuple.Create(DateTime.Now.Year, "C# 13.0");
> 
> //[!] Tuple에 의해 생성된 개체는 Item1, Item2, Item3 순서의 속성으로 접근
> Console.WriteLine($"{tuple.Item1} - {tuple.Item2}");
2025 - C# 13.0

[2]번 코드처럼 Tuple.Create() 메서드의 생성자에 값을 넣은 후 그 값의 형식을 유추해서 자동으로 튜플 개체를 만들어 낼 수도 있습니다.

C# 7.0 이후로는 Tuple 클래스의 기능 자체가 C# 언어에 포함되어 있어 괄호 기호로 바로 튜플 리터럴을 생성하는 방법으로 변경되었으니 Tuple 클래스보다는 언어에 내장된 튜플 리터럴과 튜플 반환의 기능을 사용하면 됩니다.

장 요약

이번 강의는 C# 7.0의 가장 큰 특징이 튜플에 대해서 정리를 했습니다. 튜플은 따로 데이터 형식을 사용하지 않고도 하나 이상의 데이터를 반환 값으로 전달받을 수 있는 편리함을 제공합니다.

VisualAcademy Docs의 모든 콘텐츠, 이미지, 동영상의 저작권은 박용준에게 있습니다. 저작권법에 의해 보호를 받는 저작물이므로 무단 전재와 복제를 금합니다. 사이트의 콘텐츠를 복제하여 블로그, 웹사이트 등에 게시할 수 없습니다. 단, 링크와 SNS 공유, Youtube 동영상 공유는 허용합니다. www.VisualAcademy.com
박용준 강사의 모든 동영상 강의는 데브렉에서 독점으로 제공됩니다. www.devlec.com