shyaway

Generics > Covariance and Contravariance 본문

.NET

Generics > Covariance and Contravariance

shyaway 2018. 7. 14. 07:40

Generics. Convariance and Contravariance


Convariance and contravariance are terms and also a kind of property to be able to use a specifically derived type or a less derived type than originally specified. This may not come straight to your head, let's see some codes.


Sample Model



- Cup > A base class.

- PlasticCup > A derived class implementing Cup.

- PaperCup > A dervied class implementing Cup.


Thanks for polymorphism, you can assign the value like this.


1
2
3
IEnumerable<PlasticCup> plasticCups = new List<PlasticCup>();
 
IEnumerable<Cup> cups = plasticCups;


This is possible not only for the polymorphism in OOP but also for List<T> to implement IEnumerable<T> interface and this is called convariance. What about the opposite of the convariance? It's called contravariance and the contra comes before the word, which means "Not". Something is NOT convariance... um, it seems like something is supposed to work backwards. It's safe to say that, with contravariance, we can assign the base class to the derived class, it looks like and if we have a method that takes (List<Cup> cups) as an argument, I think it's going to be okay to pass in the derived types.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Case 1
void ContravarianceTest(List<Cub> cubs) { ... };
 
// Call ContravarianceTest
{
    // Case 1-A
    ContravarianceTest(new List<PlasticCub>());
    // Case 1-B
    ContravarianceTest(new List<PaperCub>());
    // Case 1-C
    ContravarianceTest(new List<Cub>());
}
 
// Case 2
IEnumerable<PlasticCup> plasticCups = new List<Cup>();


Which cases are you 100% sure will survive the compile time type check ups? Well directly to the point, None of them will pass the screening process except Case1-C, which makes me wonder why this cannot be done? The answer will be pretty simple. IEnumerable<T> doesn't support contravariance. You've learned this lesson in a real programming life and you already know why this behavior cannot be done. Probably you can explain to other colleagues that "You know, accepting contravariance in IEnumerable<T> will result in something like this...."


1
2
3
IList<Cup> plasticCups = new List<PlasticCup>();
 
plasticCups.Add(new PaperCup()); // See the point? you just can't.... and shouldn't even try this !


If IEnumerable<T> supported contravariance, something above could have happened and this does look like a serious problem. How can you be sure that the list contains genuine PlasticCup classes only? However, there're some types that support contravariance in generics.


1
2
3
Action<Cub> cubAction = (cub) => { Console.WriteLine(cub.GetType().Name); };
Action<PlasticCub> plasticAction = cubAction;
plasticAction(new PlasticCub());


Unlike IEnumerable<T>, Action<T> allows a base type to be assigned to its derived type. It's a 100% type-safe code and it's going to certainly survive from the compile time check-ups and of course, no problem at the runtime. How can this be done? Well simply enough again, Action<T> just supports contravariance.


Now I'm seriously wondering what kind of types support convariance and contravariance and what aren't. See this table below.


Type

Convariant type parameter

Contravariant type parameter

Action<T>Yes
Comparison<T>Yes
Converter<TInput,TOutput>YesYes
Func<TResult>Yes
Func<T,TResult>YesYes
IComparable<T>Yes
Predicate<T>Yes
IComparer<T>Yes
IEnumerable<T>Yes
IEnumerator<T>Yes
IEqualityComparer<T>Yes
IGrouping<TKey,TElement>Yes
IOrderedEnumerable<TElement>Yes
IOrderedQueryable<T>Yes
IQueryable<T>Yes

Well, convariance and contravariance is probably one of the least known stuff in C#.

























'.NET' 카테고리의 다른 글

C# > Span<T>  (0) 2018.07.31
C# > Tuples  (0) 2018.07.31
IdentityServer > Storing an access token in IdentityServer3  (0) 2018.07.08
DataAnnotation > Recursive validation for collection items  (0) 2017.12.29
DataAnnotation > Make a custom attribute  (0) 2017.12.22
Comments