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

난이도 상 해설 보기 →

결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커 (A)(B) 는 주목 위치 힌트다.

결함 코드 · C#
// 주제: ref 반환(ref return) · ref 지역 변수(ref local) · ref readonly 와 인플레이스 수정
//
// 게임 서버에서 대량의 값 형식(struct) 배열(엔티티 컴포넌트 등)을 다룰 때, 복사 없이
// 원소를 제자리에서 수정하려면 ref 반환/ref 지역 변수를 쓴다. 이 코드가 어떻게
// 동작하는지(원본 배열이 바뀌는지, 복사가 일어나는지)를 예측·설명하는 문제다.
//
// 과제:
//  1) Main 의 각 출력이 무엇이 될지 순서대로 예측하라.
//  2) `ref var slot = ref pool.GetRef(1);` 로 얻은 slot 을 수정하면 왜 원본 배열이
//     바뀌는지, `var copy = pool.GetRef(1);` (ref 없이 받기)는 왜 안 바뀌는지 설명하라.
//  3) ref return 이 가리키는 저장 위치의 수명(예: 지역 배열 vs 필드 배열)과, 컴파일러가
//     막는 위험한 반환(스택 이스케이프)을 설명하라.
// 내부 동작 설명은 answer.md 에서 다룬다.

using System;

public struct Entity
{
    public int Hp;
    public int Mana;
    public override string ToString() => $"(Hp={Hp},Mana={Mana})";
}

public sealed class EntityPool
{
    private readonly Entity[] _items;
    public EntityPool(int n) { _items = new Entity[n]; }

    // 배열 원소의 저장 위치 자체를 참조로 돌려준다(복사 아님)
    public ref Entity GetRef(int i) => ref _items[i];

    // 읽기 전용 참조(수정 불가, 복사 회피)
    public ref readonly Entity Peek(int i) => ref _items[i];

    public int Count => _items.Length;
}

public static class Program
{
    public static void Main()
    {
        var pool = new EntityPool(3);

        // (1) ref 지역 변수로 받아 제자리 수정
        ref Entity slot = ref pool.GetRef(1);
        slot.Hp = 100;
        slot.Mana = 50;
        Console.WriteLine($"A {pool.GetRef(1)}");    // 원본 반영?

        // (2) ref 없이(값으로) 받은 뒤 수정하면 원본 배열은?
        Entity copy = pool.GetRef(1);
        copy.Hp = -999;
        Console.WriteLine($"B pool={pool.GetRef(1)} copy={copy}");

        // (3) ref 지역 변수를 다른 원소로 재바인딩
        slot = ref pool.GetRef(2);
        slot.Hp = 7;
        Console.WriteLine($"C idx1={pool.GetRef(1)} idx2={pool.GetRef(2)}");

        // (4) ref readonly 로 읽기(복사 없이 읽기)
        ref readonly Entity r = ref pool.Peek(1);
        Console.WriteLine($"D {r}");
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

작성 후 위 해설 보기에서 모범 해설과 대조하세요.