33. ValueTuple vs Tuple: 값 의미론·가변성·상등·이름 소거
난이도 중내 리뷰 · C#
해설 · C#
해설 — ValueTuple vs Tuple: 값 의미론·가변성·상등·이름 소거
난이도: 중
요약
출력:
A x=3 y=4
B a.hp=10 b.hp=999
C a=(10,42)
D True True
E True
F True False
(int, int) 구문은 **System.ValueTuple<...>(가변 struct, 값 형식)**이고, Tuple<...>은 불변 참조 형식이다. ValueTuple 은 복사 시 독립적이고 필드를 직접 수정할 수 있으며, 요소 이름은 컴파일 타임 장식이라 런타임 상등에 영향을 주지 않는다. 둘 다 Equals는 요소별 비교지만, ValueTuple 은 == 연산자까지 제공한다.
단계별 내부 동작
- (A):
GetSpawn()이(3,4)반환,var (x,y)=...는 분해(Deconstruct). →x=3 y=4. - (B):
var b = a;는 값 복사(struct).b.hp=999는 복사본만. →a.hp=10 b.hp=999. - (C): ValueTuple 은 가변 struct 라
a.mp=42로 필드를 직접 바꿀 수 있다. →a=(10,42). - (D): ValueTuple 은
==/!=연산자(C# 7.3+)와Equals를 요소별로 정의 → 둘 다True. - (E): 요소 이름(
first/secondvsleft/right)은 런타임 타입에 없다.named1,named2는 모두ValueTuple<int,int>라Equals가 요소별 비교로True. - (F):
Tuple<int,int>도Equals를 요소별로 오버라이드 →True. 하지만 서로 다른 힙 객체라ReferenceEquals는False.
핵심 개념
- ValueTuple: 값 형식(struct), public 가변 필드
Item1, Item2...(선택적 이름은 별칭). 스택/인라인 저장, 할당 없음.==,Equals,GetHashCode,IComparable요소별 구현. 분해(Deconstruct) 지원. - Tuple: 참조 형식(class), 불변(읽기 전용 프로퍼티
Item1...), 힙 할당.Equals는 요소별 오버라이드하지만==는 참조 비교(연산자 미제공). - 요소 이름 소거:
(hp:int, mp:int)의 이름은 컴파일러가TupleElementNamesAttribute메타데이터로만 남기고 IL 타입은ValueTuple<int,int>. 그래서 이름이 달라도 같은 타입·같은 상등.
흔한 오해·함정
- "튜플은 불변" — Tuple 은 불변이지만 ValueTuple 은 가변이다. 필드를 실수로 바꿀 수 있어, 딕셔너리 키 등으로 쓸 때 주의(값이 바뀌면 해시 위치가 어긋남 — 다만 값 복사 특성상 컨테이너에 넣으면 복사본이 저장됨).
- 박싱: ValueTuple 을
object/인터페이스로 담으면 박싱되어 힙 할당·복사 발생. 반복 경로에서object로 올리지 말 것. Tuple 은 애초에 참조형이라 별도 박싱은 없지만 생성 시 힙 할당. - 성능: 다중 반환·임시 그룹핑엔 ValueTuple 이 할당이 없어 유리. 오래 보관·불변 계약이 중요하면 record/Tuple 고려.
- 가변 struct 복사 함정: 프로퍼티가 ValueTuple 을 반환하면 그 반환값을 수정해도 원본에 반영되지 않는다(복사본 수정). ref return 이 아니면 제자리 수정 불가.
면접 포인트
(a,b)= ValueTuple(값형/가변),Tuple<>(참조형/불변)의 차이와 복사 독립성.- 요소 이름이 런타임에 소거되어(
TupleElementNamesAttribute) 상등/타입에 영향 없음. - 박싱·힙 할당 관점의 성능 차이와 다중 반환 시 ValueTuple 선호 이유.