One of the new features in C# and VB.NET is covariance and contravariance. What the heck is it?
Well it turns out it is really an easy concept and if you use object oriented features like inheritance you are using covariance and contravariance. You just don't know it as we call it by different names.
Eric Lippert a developer on the languages team has a great set articles about this complex topic. You can read them at http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx.
Basically it breaks down like this. If you have two types the types can be either equal, one smaller than the other; one larger than the other; or they are unrelated. Or in my mind the types are either eqaul i.e. the same type, superclass/subclass or unrelated. Let us assume you have two types called T0 and T1. If T1 is a subclass to T0 then you can usually use T1 values in places that call for a T0 type. This is called covariance. Contravariance then is the ability to use a value of T0 in place of something wanting a T1.
If you are like me at this point you are scratching your head going ok. But C# already does this right!? Sort of.
You would think on first glance that the following code works
IEnumerable<string> strings = new List<string>(); IEnumerable<object> objects = strings;
And if you compile this in .NET 3.5 SP1 or older you get an error. Why?
It turns out that the compiler tries to compare IEnumerable<string> to IEnumerable<object> and they are not related. What would be nice if the compiler and classes would look at them as IEnumerable string or object. Then realize that string is a subclass of object and thus the above code should work. And that is what has been done in .NET 4.
Now generics look at the type past into the generic to help figure out substitution. In .NET 4 the above will work which is what you would expect.
Why would I need contravariance. It turns out it can be very useful. Imagine for a moment you have a base class and derived class. You want to be able to example do comparison using IEqualityComparer. However in this case I want to make a method using the base type and then base in the derived class. Without contravariance this would not work.
// Simple hierarchy of classes. class BaseClass { } class DerivedClass : BaseClass { } // Comparer class. class BaseComparer : IEqualityComparer<BaseClass> { public int GetHashCode(BaseClass instance) { return instance.GetHashCode(); } public bool Equals(BaseClass x, BaseClass y) { return x == y; } } class Program { static void Test() { IEqualityComparer<DerivedClass> derivedComparer = new BaseComparer(); } }
Enjoy playing with .NET.
Comments