Programming and my thoughts

C# 으로 이런저런 프로젝트를 진행하면서... 자주 쓰진 않지만 까먹어서 여기다가 잘 까먹는 내용을 정리해두고자 한다.


* Covariance(공변성), Contravariance (반공변성)


https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance


위 사이트에 내용이 잘 정리되어 있다.


단어가 꽤 어려워 보이는데...

사실 공변성은 일반적인 Polymorphism 의 특성이라 이해하기가 쉽고, 반공변성은 직관적으로는 이해하기가 어렵다.


Covariance (공변성)

Enables you to use a more derived type than originally specified.


Contravariance (반공변성)

Enables you to use a more generic (less derived) type than originally specified.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Base { }
public class Derived : Base { }
 
public static Derived MyMethod(Base b)
{
    return b as Derived ?? new Derived();
}
 
Func<Base, Derived> f1 = MyMethod;
 
// Covariant return type.
Func<Base, Base> f2 = f1;
Base b2 = f2(new Base());
 
// Contravariant parameter type.
Func<Derived, Derived> f3 = f1;
Derived d3 = f3(new Derived());
cs


아래는 또 다른 예...


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Covariance.   
IEnumerable<string> strings = new List<string>();
// An object that is instantiated with a more derived type argument   
// is assigned to an object instantiated with a less derived type argument.   
// Assignment compatibility is preserved.   
IEnumerable<object> objects = strings;
 
// Contravariance.             
// Assume that the following method is in the class:   
void SetObject(object o) { }   
Action<object> actObject = SetObject;
// An object that is instantiated with a less derived type argument   
// is assigned to an object instantiated with a more derived type argument.   
// Assignment compatibility is reversed.   
Action<string> actString = actObject;
cs


Covariance 의 경우 좌변의 부모가 우변의 자식을 품는 형태가 되는데...

Contravariance 의 경우 좌변의 자식이 우변의 부모를 품는 형태가 된다.

* Contravariance 는 직관적이지 않아서 사실 이해하기가 어렵다.. C# 에서 지원하는 기능중 하나이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Covariant interface.
interface ICovariant<out R> { } // 1.
 
// Extending covariant interface.
interface IExtCovariant<out R> : ICovariant<R> { } // 2.
 
// Implementing covariant interface.
class Sample<R> : ICovariant<R> { }
 
ICovariant<Object> iobj = new Sample<Object>();
ICovariant<String> istr = new Sample<String>();
 
// You can assign istr to iobj because
// the ICovariant interface is covariant.
iobj = istr;
cs

 

1. interface ICovariant 를 generic 형태로 만들었는데 out R 이라는 인자를 받게끔 되어 있다.

2. 그리고 ICovariant 를 상속받은 IExtCovariant 도 같은 형태를 취하게 된다.


코드 마지막의 iobj = istr 은 Covariance 의 특성이다.


정리하긴 했는데... 쓸 일은 별로 없었던 것 같다.

어떤 언어만의 특성이 강하게 반영된 코드를 작성하면... 그 언어가 제공하는 특성을 최대한 활용할 수 있는 장점이 있지만...

나중에 그 언어에 익숙하지 않은 다른 후임 개발자가 이 코드를 보게되면 유지보수가 어려운 단점이 있다고 생각한다.


1. 따라서 다른 개발자에게 공개할 필요가 없고, 캡슐화가 가능한 코드에는 이런 방법을 고려해보면 좋을 것 같고...

2. 많은 개발자와 공동작업을 진행하고 앞으로 후임 개발자의 투입도 고려해야 하는 상황이라면... 복잡하고 난해한 코드들은 아무리 기능이 좋아도 오히려 독이 된다고 생각한다.