본문 바로가기

.NET Framework/C# 4.0 Quick Look

[C# 4.0] New Features in C# : 07. 공변성과 반공변성(Covariance and Contravariance)


 회차
[C# 4.0] New Features in C# : 01. C# Programing Trend
[C# 4.0] New Features in C# : 02. C# 4.0 Overview
[C# 4.0] New Features in C# : 03. Dynamically Typed Objects #1 : DLR
[C# 4.0] New Features in C# : 04. Dynamically Typed Objects #2 : Dynamic Lookup
[C# 4.0] New Features in C# : 05. Optional and Named Parameters
[C# 4.0] New Features in C# : 06. Com-specific interop features
[C# 4.0] New Features in C# : 07. Covariance and Contravariance

07| 공변성과 반공변성(Covariance and Contravariance)
구독자 여러분 안녕하세요

오늘 다뤄볼 내용은 공변성과 반공변성입니다.
" 공변성, 반 공변성은 도대체 뭘까? "  Variance & ContraVariance 두단어를 볼 때 무슨 소리인지 알 수가 없을것입니다.


물론 수학을 많이 다뤄보셨다면 무슨 의미인지 알 수는 있지만 대부분의 개발자분들은 무심코 지나치셨거나 모른체 사용을 해왔을 것 같네요. 공변성, 반공변성은 수학에서 정의한 원리를 바탕으로 부모는 자식을 받아 드일수 있으나 자식은 부모를 받아 드릴 수 없는 원칙을 말하고 있습니다.


즉, 공변성은 원래 지정된 것 보다 더 많은 파생을 사용할수 있는 이며, 반공변성은 더 작은 파생 형식을 사용 할 수 있는 기능을 뜻합니다.

말이 어렵죠? 이해를 돕고자 일반적인 프로그래밍 이야기를 해보도록 하겠습니다. 클래스의 상속은 부모 클래스로부터 상속을 받아 메소드나 멤버 변수를 그대로 물려 받을 수 있을 텐데요.

자식은 양적으로 부모보다 더 많이 가지게 되며 반면에 부모클래스는 상속받은 자식과 비교를 한다면 적게 가진다는 결론을 내릴수 있습니다.

그렇다면 부모 클래스에 자식 클래스를 캐스팅 한다면 어떻게 될까요?

다형성 원칙에 의해 부모 클래스를 가지고 있는 기능을 제외한 추가된 자식의 기능은 무시가 되버리고 어쨌든 문제는 없어 보입니다. 반대로 자식을 부모에 대입하려고 한다면 형 변환에 대한 예외에러가 출력이 되겠죠. 지금부터 Genric covariance And Contra-Variance에 대해 설명을 하려면 그동안 어떻게 개선이 되고 있었는지 살펴보아야할것 같습니다.

* Variance in Arrays (C# 1.0)
C# 1.0에서부터 variance in Arrays라는 개념이 등장하게 되었습니다.

[코드 1] Variance in Arrays (C# 1.0) Sample
public class Animal { ... }
public class Lion : Animal { ... }
public class Deer : Animal { ... }
public class Cat : Animal { ... }

Animal[] animals = new Lion[3];
Animals[0] = new Deer();                      // Runtime시 Exception 발생

현재 보시는 코드는 전형적인 상속이 되는 코드입니다. 1.0에서는 위와 같이 Reference 타입을 할당하려고 할 때 covariance라고 하였습니다. Animal로 부터 상속  받은 사자와 사슴과 고양이는 Animal 스택틱 영역에  객체로 할당 할 수 있습니다.

여기서 살펴보셔야할 내용은 마지막 두번째 줄 코드인데요.
Animals 인스턴스에 사자를 할당을 하려고 하고 있는데. 이어서 마지막 코드에서 사자 우리에 사슴을 할당하려고 합니다.

일반적으로 사자 우리에 사슴을 넣으면 안되잖아요. 그럼 잡혀먹히잖아요...흑흑..ㅠㅠ


이처럼 C# 1.0 버전에서는 타입에 대한 불확실한 요소에 대해 보호 할 수 없었습니다. 

* Variance in Delegate-Member Association(C# 2.0)
그렇게 C# 2.0에서 제네릭과 대리자가 도입이 되면서 이전 버전의 불안정한 요소를 벗어 날 수 있게 되었습니다.
이들을 통해서 컴파일 타임에서 엄격한 체크가 수행이 되었고 안전한 코드를 작성 할 수 있게 되었습니다. 하지만 이때부터 공변성과 반공변성은 애매모한 관계를 가지게 되었죠.

코드를 보시면서 좀더 이해를 해보도록 하겠습니다.

[코드 2] Variance in Delegate-Member Association(C# 2.0) Sample
class Mammals { ... }
class Dogs : Mammals { ... }
public delegate Mammals HandlerMethod();
public static Mammals FirstHandler() { return null; }
public static Dogs SecondHandler() { return null; }

public Form1() {
             InitializeComponent();
 1)....   HandlerMethod handler = SecondHandler;
 2)....   this.textBox1.KeyDown += this.MultiHandler;
 3)....   this.button1.MouseClick += this.MultiHandler;

}
private void MultiHandler(object sender, System.EventArgs e) { ... }

현재 코드에서는 1번의 경우가 공변성이 되며, 2,3번의 경우는 반공변성이 되는 예제입니다.

* 1번의 경우
대리자를 사용한 경우입니다만 대리자 자신이 포함 할 수 있는 형식인 Mammals에 대해서만 해석을 할 수 가 있기 때문에 공변성 이락 할수 있습니다.

* 2,3번의 경우
이미 선언된 형식에 호환성을 맞추기 위해서 역으로 해석한 모습을 볼 수가 있는데요. EventArgs로부터 KeyEventArgs와 MouseEventArgs로 역으로 호환성을 맞추려고 하기 때문에 반공변성이 될 수 있습니다.

* Variance in Generic Delegate (C# 3.0)
그렇게 2.0에서 애모한 관계를 개선하고자 3.0이 등장을 하게 되었는데요.
이때는 제네렉과 대리자를 통해 공변성이 많이 지원이 되었습니다. 이말은 상위 클래스가 존재를 한다면 하위 클래스가 상위 클래스를 모두 가져 올 수 있도록 IEnumerable 인터페이스를 사용할수 있게 되어 covariance를 지원 하게 되었습니다.

지원을 하였지만 아래코드를 보시면 공변성에 대한 모순이 발생을 하게 되었습니다.
[코드 3] Variance in Generic Delegate (C# 3.0) Exception Sample
IList<string> strings = new List<string>();
IEnumberable<object> objects = strings;           ... 1)  // Exception 발생
var result = string.Union(objects);                    ... 2)  // Exception 발생


1번과 2번의 경우 공변성을 하려고 하는데 Exception이 발생되는 모습을 볼 수 있었습니다.

* Generic Covariance And Contra Variance(C# 4.0)
3.0에서 발생하게 된 이슈를 해결 하기 위해 4.0에서는 Generic Covariance And Contra Variance이 등장하게 됩니다.
코드를 보시면 새로운 형식을 볼 수 있을텐데요.

[코드 4] Generic Covariance And Contra Variance(C# 4.0) Sample
class Animal {}
class Cat : Animal { }
class Program
{
       delegate void ActionDemo<in T>(T a);
       delegate T FuncDemo<out T>();

        static void Main()
        {
                //Contra-Variance
                ActionDemo<Animal> act1 = (ani) => { Console.WriteLine(ani); };
                ActionDemo<Cat> cat1 = act1;
                cat1(new Cat());

                //CoVariance
                FuncDemo<Cat> cat = () => new Cat();
                FuncDemo<Animal> animal = cat;

                Console.WriteLine(animal());
        }
}

3.0에서 모순이 된 IEnumberable 인터페이스의 문제를 해결하면서 애모했던 공변성과 반공변성에 대해 새로운 형식인 IN, OUT 키워드를 도입하면서 관계를 해소 할 수 있게 되었습니다.

- Contra-Variance를 하시려면 IN 키워드를 사용
- Co-Variance를 하시려면 OUT 키워드를 사용

그외 인터페이스와 델리게이트는 다음과 같으며 자세한 사항은 MSDN 문서를 참고 바랍니다.

* InterFaces
- System.Collections.Generic.IEnumberable<out T>
- System.Collections.Generic.IEnumberator<out T>
- System.Linq.IQueryable<out T>
- System.Collections.Generic.IComparer<in T>
- System.Collections.Generic.IEqualityComparer<in T>
- System.IComparable<int T>

* Delegates
- System.Func<int T, ..., out R>
- System.Action<in T, ...>
- System.Predicate<in T>
- System.Comparison<in T>
- System.EventHandler<in T>

[소스 참고]Hello .NET Framework 4 세미나 [세션3] 발표자료와 소스 : http://blog.tobegin.net/35

참고 문헌
1. PDC2008 : The Future of C# - Anders Hejlsberg
    : http://channel9.msdn.com/pdc2008/TL16/

2. New Features in C# 4.0 - Mads Torgersen, C# Language PM, April 2010
    :
http://msdn.microsoft.com/ko-kr/vcsharp/ff628440(en-us).aspx
3. 제네릭의 공변성(Covariance)과 반공변성(Contravariance)
    :
http://msdn.microsoft.com/ko-kr/library/dd799517.aspx


포스팅을 마치며...

유익한 포스팅이 되셨는지 모르겠네요.

더 궁금하신점이 있으시면 댓글 혹은
http://blog.tobegin.net/notice/1 프로필정보를 통해 문의 바랍니다.
감사합니다.

정은성 드림

PS. 이번주는 태풍이 온다네요. 우산 꼭 챙기세요 ^ㅁ^