← 문제로

32. ref 반환·ref 지역 변수·ref readonly 와 인플레이스 수정

난이도 상
내 리뷰 · C#
해설 · C#

해설 — ref 반환·ref 지역 변수·ref readonly 와 인플레이스 수정

난이도: 상

요약

출력:

A (Hp=100,Mana=50)
B pool=(Hp=100,Mana=50) copy=(Hp=-999,Mana=50)
C idx1=(Hp=100,Mana=50) idx2=(Hp=7,Mana=0)
D (Hp=100,Mana=50)

ref Entity GetRef(int i) => ref _items[i] 는 원소의 **저장 위치(별칭)**를 돌려준다. ref 지역 변수로 받으면 그 위치를 직접 가리켜 제자리 수정이 원본 배열에 반영되고(복사 없음), ref 없이 받으면 값 형식이 복사되어 원본과 분리된다. ref readonly는 복사 없이 읽되 수정을 금지한다.

핵심 개념

  • ref return: 값이 아니라 "그 값이 사는 저장 위치"를 반환한다(배열 원소, 필드 등 lvalue). 큰 struct 를 복사 없이 제자리 수정/읽기 할 수 있어 ECS 류 고성능 코드에 유용.
  • ref local: ref var x = ref expr; 로 저장 위치를 별칭한다. 이후 x.Field = ...는 원본 위치를 바꾼다. x = ref other;재바인딩도 가능(C# 7.3+).
  • ref readonly / in: 읽기 전용 별칭. 복사를 피하면서 수정은 막는다(방어적 복사 이슈는 readonly struct 문제 참조).

단계별 내부 동작

  1. (A): slot_items[1]을 별칭. slot.Hp=100; slot.Mana=50이 배열 원소를 직접 갱신 → (Hp=100,Mana=50).
  2. (B): Entity copy = pool.GetRef(1); — 반환은 ref 지만 받는 쪽이 값 타입 변수라 그 시점에 복사가 일어난다. copy.Hp=-999는 복사본만 바꾼다. pool은 그대로 (Hp=100,Mana=50), copy=(Hp=-999,Mana=50).
  3. (C): slot = ref pool.GetRef(2);slot_items[2]재바인딩(값 대입이 아니라 별칭 변경). slot.Hp=7_items[2]를 바꿈. idx1(=_items[1])은 그대로 (Hp=100,Mana=50), idx2=(Hp=7,Mana=0).
  4. (D): ref readonly_items[1]을 복사 없이 읽음 → (Hp=100,Mana=50).

흔한 오해·함정

  • Entity copy = pool.GetRef(1); 는 여전히 복사다. ref return 을 받으려면 받는 쪽도 ref(또는 ref readonly)여야 별칭이 유지된다. ref 없이 받으면 그 자리에서 값 복사.
  • slot = pool.GetRef(2);(ref 없음) vs slot = ref pool.GetRef(2);(ref 있음) 는 완전히 다르다: 전자는 slot이 가리키는 위치(_items[1])에 _items[2]의 값을 복사 대입(원본 _items[1]을 덮어씀), 후자는 slot을 _items[2]로 재바인딩. 이 구분이 ref local 의 가장 헷갈리는 부분.
  • 스택 이스케이프 방지: ref return 은 호출자보다 오래 사는 저장 위치만 가리킬 수 있다. 지역 변수/stackalloc/메서드 파라미터(값)의 주소를 ref 로 반환하려 하면 컴파일러가 거부한다(ref safety, ref struct/Span<T> 규칙과 동일 계열). 필드/배열 원소는 객체 수명에 묶여 안전.
  • ref 필드(C# 11)와 scoped는 이 안전성 규칙을 더 세분화한다.

면접 포인트

  • ref return 이 "값이 아니라 저장 위치"를 돌려준다는 점과, 받는 쪽이 ref여야 별칭이 유지된다는 점.
  • slot = expr(값 대입)과 slot = ref expr(재바인딩)의 의미 차이.
  • ref safety(스택 이스케이프 방지)와 왜 지역/stackalloc 주소를 반환할 수 없는지, ECS·Span<T> 고성능 시나리오에서의 활용.