17. 제네릭 가변성(공변 out / 반공변 in)과 배열 공변성의 차이

난이도 중 해설 보기 →

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

결함 코드 · C#
// 주제: 제네릭 가변성(variance) — 인터페이스 공변(out)/반공변(in) 과 배열 공변성의 차이.
//
// IEnumerable<out T> 는 공변, Action<in T> 는 반공변이다. 반면 List<T> 는 불변(invariant)이고,
// 배열은 (역사적 이유로) 공변이지만 그 공변은 타입 안전하지 않다.
//
// 과제:
//   1) 이 프로그램의 정확한 표준출력을 예측하라(네 줄).
//   2) (1) 공변, (2) 반공변 대입이 각각 왜 컴파일되는지(out/in 의 의미)를 설명하라.
//   3) (3) 배열 공변성은 왜 컴파일은 되는데 런타임 예외가 나는지, 인터페이스 공변성과
//      무엇이 다른지 설명하라.
//   4) 주석 처리된 List<Animal> 대입이 왜 컴파일 에러인지(List<T> 가 불변인 이유) 설명하라.

using System;
using System.Collections.Generic;

class Animal { public virtual string Name => "Animal"; }
class Cat : Animal { public override string Name => "Cat"; }

class Program
{
    static void PrintAll(IEnumerable<Animal> animals)
    {
        foreach (var a in animals) Console.Write(a.Name + " ");
        Console.WriteLine();
    }

    static void Main()
    {
        // (1) 인터페이스 공변성: IEnumerable<out T>
        List<Cat> cats = new List<Cat> { new Cat(), new Cat() };
        IEnumerable<Animal> asAnimals = cats;
        PrintAll(asAnimals);

        // (2) 인터페이스 반공변성: Action<in T>
        Action<Animal> printAnimal = a => Console.WriteLine("animal " + a.Name);
        Action<Cat> assigned = printAnimal;
        assigned(new Cat());

        // (3) 배열 공변성(불안전)
        Animal[] arr = new Cat[2];
        try
        {
            arr[0] = new Animal();
            Console.WriteLine("stored Animal");
        }
        catch (ArrayTypeMismatchException)
        {
            Console.WriteLine("ArrayTypeMismatchException");
        }

        // (4) List<T> 는 불변: 아래는 컴파일 에러라 주석 처리
        // List<Animal> bad = cats;   // CS0266

        Console.WriteLine("done");
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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