방프리

21.03.07 Chapter3. 제네릭 활용 (Item 22) 본문

C#/Effective C#

21.03.07 Chapter3. 제네릭 활용 (Item 22)

방프리 2021. 3. 7. 20:58

Item 22 : 공변성과 반공변성을 지원하라

 

  • 공변성과 반공변성은 무엇일까? 단어부터 생소하다. 타입의 가변성으로도 말하는데 특정 타입의 객체를 다른 타입의 객체로 변환할 수 있는 성격을 말한다.
abctract public class CelestialBody : IComparable<CelestialBody>
{
	public double Mass { get; set; }
	public string Name { get; set; }
}

public class Planet : CelestialBody
{
}

public class Moon : CelestialBody
{
}

public class Asteroid : CelestialBody
{
}

[예제에 사용될 클래스들]

  • 공변성
    • X를 Y로 바꾸어 사용할 수 있는 경우, C<X>를 C<Y>로도 바꾸어 사용할 수 있다면 C<T>는 공변이다.
    • 출력위치에서만 사용되는데 이 때 출력위치는 함수의 반환값, 속성의 get 접근자, 델리게이트의 일부 위치이다.
    • IEnumerator<T>를 정의할 때 T에 대해 out 데코레이터가 사용됨 (하단의 코드 참조)
public interface IEnumerable<out T> : IEnumerable
{
	new IEnumerator<T> GetEnumerator();
}

public interface IEnumerator<out T> : IDisposable, IEnumerator
{
	new T Current { get; }
}

 

공변성의 예

public static void CoVariantArray(CelestialBody[] baseItems)
{
    foreach (var thing in baseItems)
    {
    	Console.WriteLine($"{this.Name} has a mass of {thing.Mass} Kg");
    }
    //반환되는 타입이 정확하므로 에러를 발생하지 않는다.
}
public static void UnsafeVariantArray(CelestialBody[] baseItems)
{
    baseItems[0] = new Asteroid
    { Name = "Hygiea", Mass = 8.85e19 };
        
    //자식 클래스여도 타입이 다르기 때문에 예외를 일으킨다.
    
    CelestialBody[] spaceJunk = new Asteroid[5];
    spaceJunk[0] = new Planet();
    //마찬가지로 타입이 다르기 때문에 예외가 발생
}

 

  • 반공변성
    • Y를 X로 바꾸어 사용할 수 잇는 경우 C<X>를 C<Y>로 바꾸어 사용할 수 있다면 C<T>는 반공변이다.
    • 입력위치에서만 사용되는데 이 때 입력위치는 함수의 매개변수, 속성의 set 접근자, 델리게이트의 일부 위치이다.
    • IComparable<T> 인터페이스가 in 데코레이터를 사용 (하단의 코드 참조)
public interface IComparable<in T>
{
	int CompareTo(T other);
}

 

  • 델리게이트의 매개변수
    • 델리게이트의 매개변수에 공변/반공변을 지정하는 것은 어려운 작업은 아니지만 사용시 인지 문제가 생길 수 있다. (반대로 쓸 가능성이 있음)
    • 컴파일러 단에서 오류를 검출하고 싶다면 반드시 in, out 키워드를 적극 활용하자.

 

Comments