1. 튜플 네이밍: 디컨스트럭션을 위한 컨텍스트
이전 강의에서 이미 튜플에 이름을 붙여서 Item1, Item2 같은 의미 없는 이름 대신, person.Name처럼 의미 있는 이름으로 값에 접근할 수 있다는 걸 배웠지. 튜플 요소에 이름을 붙이는 건, 특히 메서드의 반환값이나 파라미터, 프로퍼티로 쓸 때 코드가 훨씬 읽기 쉬워져서 중요해. 네이밍이 바로 우리가 이 강의에서 배울 디컨스트럭션을 편하게 해주는 핵심 전제야.
참고로, 이름은 튜플 리터럴로 초기화할 때나, 메서드/프로퍼티/필드의 반환 타입 시그니처에서 지정할 수 있어.
메서드 예시: 메서드 반환값에서 네이밍된 튜플 요소
public static (int 나이, string 이름) GetPetInfo()
{
return (나이: 5, 이름: "Барсик");
}
// 사용 예시:
var info = GetPetInfo();
Console.WriteLine($"{info.이름} — {info.나이} 살");
필드/프로퍼티 예시: 필드/프로퍼티에서 네이밍된 튜플 요소
public (int 너비, int 높이) ImageSize = (1024, 768);
기억해: 이름을 명시적으로 지정하지 않으면, 요소는 여전히 Item1, Item2 등으로 접근해야 해. 이러면 튜플을 계속 쓸 때 코드가 덜 읽기 쉬워지니까, 의미가 명확하지 않으면 항상 이름을 붙여주는 게 좋아.
2. 튜플 디컨스트럭션
디컨스트럭션이 뭐야?
디컨스트럭션은 튜플을 여러 변수로 "분해"해서 각각 편하게 쓰는 거야. 예를 들어, (나이: 5, 이름: "Барсик") 튜플에서 나이랑 이름 두 변수를 바로 뽑아낼 수 있지.
비유: 튜플이 이름표 붙은 박스라면, 디컨스트럭션은 그 박스 안에 든 걸 바로 책상 위에 꺼내놓는 거랑 똑같아.
디컨스트럭션 문법
var pet = (나이: 5, 이름: "Барсик");
var (age, name) = pet;
Console.WriteLine($"{name} — {age} 살");
이제 age랑 name 두 변수가 생겼어. 이 변수들은 튜플에서 값을 받아온 거고, 왼쪽에 쓴 이름(age, name)은 튜플 안 이름이랑 달라도 돼. 그냥 새로운 로컬 변수일 뿐이야.
함수 반환값 디컨스트럭션
함수에서 여러 값을 반환할 때 튜플을 많이 쓰는데, 이럴 때 디컨스트럭션이 특히 편해:
public static (double 최소값, double 최대값) GetMinMax(int[] data)
{
int 최소값 = data.Min();
int 최대값 = data.Max();
return (최소값, 최대값);
}
var numbers = new[] { 1, 2, 3, 4, 5 };
var (minValue, maxValue) = GetMinMax(numbers);
Console.WriteLine($"최소: {minValue}, 최대: {maxValue}");
디컨스트럭션할 때 변수 이름은 상황에 맞게 네가 원하는 대로 지으면 돼.
var로 디컨스트럭션
var (a, b) = (10, 20); // int a = 10, b = 20
디컨스트럭션과 discard _
가끔 튜플의 모든 요소가 필요하지 않을 때가 있어. 이럴 땐 _ (discard)로 무시할 수 있어. 디스카드(_)는 "여기 값이 있는 건 아는데, 난 변수 안 만들 거야"라는 뜻이야.
즉, 튜플을 디컨스트럭션하면서 필요 없는 자리에 "구멍"을 뚫는 거지. 간단하고 깔끔하게 쓸 수 있어!
var pet = (나이: 5, 이름: "Барсик", 행복함: true);
var (age, _, isHappy) = pet; // age랑 isHappy만, 이름은 무시
꿀팁: 디스카드는 네임스페이스에 필요 없는 변수가 생기는 걸 막아줄 때 자주 써!
foreach에서 디컨스트럭션
C#에서는 튜플 배열을 foreach에서 바로 디컨스트럭션하면서 순회할 수 있어:
var pets = new (string 이름, int 나이)[]
{
("Барсик", 5),
("Муся", 3),
("Джонни", 7)
};
foreach (var (name, age) in pets)
{
Console.WriteLine($"{name} — {age} 살");
}
3. 요소 이름과 타입 지정이 어떻게 동작하는지
네이밍된 요소의 동작
튜플 요소의 이름은 "문법 설탕"이야. 즉, 사람을 위한 편의 기능이지. 컴파일할 때 이름은 내부적으로 Item1, Item2 등으로 바뀌지만, IDE랑 컴파일러가 이름을 기억해서 네가 쓸 수 있게 해줘.
타입 호환성과 변환에 미치는 영향
요소 개수랑 타입이 같으면, 이름이 달라도 두 튜플은 같은 타입으로 취급돼. 즉, 요소 이름은 타입 정의에 포함되지 않아:
var t1 = (X: 42, Y: 13);
var t2 = (A: 42, B: 13);
t1 = t2; // OK
Console.WriteLine(t1.X); // 42
하지만, 코드에서 쓰거나 IntelliSense 힌트에서는 항상 왼쪽(할당받는 쪽) 이름이 보여.
암시적/명시적 네이밍
이미 배웠지만, 다시 말하면 튜플을 일부만 네이밍하거나 아예 이름 없이 만들 수도 있어. 이름이 없으면 그냥 Item1 등으로 접근해야 해.
var point = (X: 10, 20); // X랑 Item2
Console.WriteLine(point.X); // 10
Console.WriteLine(point.Item2); // 20
팁: 튜플에 요소가 2개 이상이거나, 값의 의미가 명확하지 않으면 항상 이름을 붙여줘.
4. 네이밍과 디컨스트럭션에서 자주 하는 실수와 특징
실수 1: 다른 튜플 할당 시 이름이 "이동"되는 현상
처음에 이름 붙인 튜플을 만들고, 나중에 이름 없는(혹은 다른 이름의) 튜플을 할당하면, IntelliSense에는 원래 이름이 계속 보여. 예시:
var original = (X: 1, Y: 2);
var alias = original; // alias.X == 1, alias.Y == 2
original = (10, 20); // 이름 없는 튜플
Console.WriteLine(alias.X); // 여전히 동작, alias는 예전 이름을 유지
실수 2: 요소 이름 중복
두 요소에 같은 이름을 붙이면 안 돼 — 컴파일러가 "Duplicate tuple element name" 에러를 뱉어:
var badTuple = (A: 1, A: 2); // 에러 CS8122: Duplicate tuple element name 'A'
실수 3: 요소 개수 안 맞는 디컨스트럭션
디컨스트럭션할 때 변수 개수랑 튜플 크기가 정확히 맞아야 해. 안 맞으면 에러 나:
var pet = (나이: 5, 이름: "Барсик");
var (age, name, mood) = pet; // 에러 CS8124: Tuple must contain exactly 3 elements
실수 4: discard _ 잘못 쓰기
_로 요소를 무시할 때, 각각 따로 써야 하고, 여러 개를 한 번에 건너뛰는 건 안 돼. 예를 들어, 두 개를 한 _로 건너뛰려 하면 에러야:
var data = (1, 2, 3);
var (_, x, _) = data; // OK: 첫 번째, 세 번째 무시
var (_, _) = data; // 에러 CS8124: Tuple must contain exactly 2 elements
GO TO FULL VERSION