Covariance and Contravariance FAQ
In this post I’ll try to answer the most common questions I find on forums and in documentation feedback about C# covariance and contravariance. It’s a big topic for a single blog post, so expect to see a lot of “more information” links.
Special thanks to Eric Lippert and Chris Burrows for reviewing and providing helpful comments.
In C#, covariance and contravariance enable implicit reference conversion for array types, delegate types, and generic type arguments.Covariance preserves assignment compatibility and contravariance reverses it.
The following code demonstrates the difference between assignment compatibility, covariance, and contravariance.
// Assignment compatibility.
string str = "test";
// An object of a more derived type is assigned to an object of a less derived type.
object obj = str;
// 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 I have this method:
// static 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;
In C#, variance is supported in the following scenarios:
- Covariance in arrays (since C# 1.0)
- Covariance and contravariance in delegates, also known as “method group variance” (since C# 2.0)
- Variance for generic type parameters in interfaces and delegates (since C# 4.0)
Arrays are covariant since C# 1.0. You can always do the following:
object[] obj = new String[10];
In the above code, I assigned an array of strings to an array of objects. So I used a more derived type than that originally specified, which is covariance.
Covariance in arrays is considered “not safe,” because you can also do this:
obj[0] = 5;
This code compiles, but it throws an exception at run time because obj is in fact an array of strings and cannot contain integers.
This feature was added in C# 2.0. When you instantiate a delegate, you can assign it a method that has a more derived return type than that specified in the delegate (covariance). You can also assign a method that has parameter types less derived than those in the delegate (contravariance).
Here’s a quick code example illustrating the feature and some of its limitations.
static object GetObject() { return null; }
static void SetObject(object obj) { }
static string GetString() { return ""; }
static void SetString(string str) { }
static void Main()
{
// Covariance. A delegate specifies a return type as object,
// but I can assign a method that returns a string.
Func<object> del = GetString;
// Contravariance. A delegate specifies a parameter type as string,
// but I can assign a method that takes an object.
Action<string> del2 = SetObject;
// But implicit conversion between generic delegates is not supported until C# 4.0.
Func<string> del3 = GetString;
Func<object> del4 = del3; // Compiler error here until C# 4.0.
}
By the way, this feature works for all delegates, both generic and non-generic, not just for Func and Action delegates.
For more information and examples, see Covariance and Contravariance in Delegates on MSDN and Eric Lippert’s post Covariance and Contravariance in C#, Part Three: Method Group Conversion Variance.
This is a new feature in C# 4.0. Now, when creating a generic interface, you can specify whether there is an implicit conversion between interface instances that have different type arguments. For example, you can use an interface instance that has methods with more derived return types than originally specified (covariance) or that has methods with less derived parameter types (contravariance). The same rules are applied to generic delegates.
While you can create variant interfaces and delegates yourself, this is not the main purpose for this feature. What is more important is that a set of interfaces and delegates in .NET Framework 4 have been updated to become variant.
Here’s the list of updated interfaces:
- IEnumerable<T> (T is covariant)
- IEnumerator<T> (T is covariant)
- IQueryable<T> (T is covariant)
- IGrouping<TKey, TElement> (TKey and TElement are covariant)
- IComparer<T> (T is contravariant)
- IEqualityComparer<T> (T is contravariant)
- IComparable<T> (T is contravariant)
And the list of updated delegates:
- Action delegates from the System namespace, for example, Action<T> and Action<T1, T2> (T, T1, T2, and so on are contravariant)
- Func delegates from the System namespace, for example, Func<TResult> and Func<T, TResult> (TResult is covariant; T, T1, T2, and so on are contravariant)
- Predicate<T> (T is contravariant)
- Comparison<T> (T is contravariant)
- Converter<TInput, TOutput> (TInput is contravariant; TOutput is covariant.)
The most frequent scenario for most users is expected to be something like this one:
IEnumerable<Object> objects = new List<String>();
While this code doesn’t look that impressive, it allows you to reuse a lot of methods that accept IEnumerable objects.
class Program
{
// The method has a parameter of the IEnumerable<Person> type.
public static void PrintFullName(IEnumerable<Person> persons)
{
// The method iterates through a sequence and prints some info.
}
public static void Main()
{
List<Employee> employees = new List<Employee>();
// I can pass List<Employee>, which is in fact IEnumerable<Employee>,
// although the method expects IEnumerable<Person>.
PrintFullName(employees);
}
}
A couple of important rules to remember:
- This feature works only for generic interfaces and delegates. If you implement a variant generic interface, the implementing class is still invariant. Classes and structs do not support variance in C# 4.0.
So the following doesn’t compile:
// List<T> implements the covariant interface
// IEnumerable<out T>. But classes are invariant.
List<Person> list = new List<Employee>(); // Compiler error here. - Variance is supported only if a type parameter is a reference type. Variance is not supported for value types.
The following doesn’t compile either: // int is a value type, so the code doesn't compile.
IEnumerable<Object> objects = new List<int>(); // Compiler error here.
I wrote a couple of MSDN topics that show how you can benefit from this new feature. They might help you better understand the principles of covariance and contravariance:
- Using Variance in Interfaces for Generic Collections
- Using Variance for Func and Action Generic Delegates
Also, take a look at the video How Do I: Use Covariance and Contravariance in VS 2010 Part I? by Eric Lippert.
The out keyword marks a type parameter as covariant, and the in keyword marks it as contravariant. The two most important rules to remember:
- You can mark a generic type parameter as covariant if it is used only as a method return type and is not used as a type of formal method parameters.
- And vice versa, you can mark a type as contravariant if it is used only as a type of formal method parameters and not used as a method return type.
For more information about variance validation, read Creating Variant Generic Interfaces and Variance in Delegates on MSDN and Eric Lippert’s post Exact rules for variance validity.
This example shows how to create a variant generic interface:
interface IVariant<out R, in A>
{
// These methods satisfy the rules.
R GetR();
void SetA(A sampleArg);
R GetRSetA(A sampleArg);
// And these don’t.
// A GetA();
// void SetR(R sampleArg);
// A GetASetR(R sampleArg);
}
If you extend a variant interface, the extending interface is invariant by default. You must explicitly specify whether the type parameters are covariant or contravariant by using the out or in keyword. Here is a quick example from MSDN:
interface ICovariant<out T> { }
// This interface is invariant because I didn't use the "out" keyword.
interface IInvariant<T> : ICovariant<T> { }
// And this one is covariant because I explicitly specified this.
interface IExtCovariant<out T> : ICovariant<T> { }
And once again, this feature is supported for generic interfaces and delegates only. So the following doesn’t compile:
class Sample<out T> { } // Compiler error here.
For more examples, take a look at the video How Do I: Use Covariance and Contravariance in VS 2010 Part II? by Eric Lippert.
This is the MSDN root topic: Covariance and Contravariance.
And, of course, read Eric Lippert’s blog. He designed this feature for C# 4.0, so who knows more about it?
Anonymous
February 17, 2010
You have a great method in explanation, keep going please...Anonymous
February 19, 2010
You have misspelled the comment - // CovarainceAnonymous
February 19, 2010
@Abdullah Thanks! I'll do my best :-) @Mohit Thanks, fixed. It's my favorite typo, I keep making it over and over.Anonymous
February 22, 2010
Why isn't IList<> updated for variance?Anonymous
March 03, 2010
nice article. very well explained.thanx...Anonymous
March 03, 2010
why the variance is not supported on classes? An simple example : Public class Employee : Person{ } //a function that acts on employee list public void CountNamesWithA(IEnumerable<Person> ) can I pass to CountNamesWithA an IEnumerable<Employee> ?Anonymous
March 04, 2010
The best thing about Covariance/Contravariance and the C# 4.0 compiler is that, you can now use Covariance and Contravariance in any .Net framework project which targets .Net Framework 2.0 or above. Just used [co/contra] variance in .Net framework 3.5 application.Anonymous
March 04, 2010
@ Andrei If we had variance on classes, we'd get exactly the same problem as with arrays. It would not be safe. Imagine this: List<Person> persons = new List<Employee>(); persons.Add(new Customer()); Compiler would not say anything, because Customer is Person. But since Employee is not Customer, you would get a run-time exception. This is a very common question, by the way... I probably need to add an answer to the FAQ itself.Anonymous
March 04, 2010
@Jay Bazuzi: "Why isn't IList<> updated for variance?" I'm sure Microsoft would have updated it if they could. But IList<T>, having the T parameter used in both return types and method parameter types, is actually invariant. It's the same reason that List<T> can't be variant, even if C# 4.0 did support variance for types. To see why IList<T> and List<T> are both invariant, just consider what would happen: List<Animal> list = new List<Animal>(); List<object> list2 = list; // error List<Dog> list3 = list; // error You can't make the assignment for list2, because then you'd be able to call list2.Add() and try to add any arbitrary System.Object to a list that should contain only Animals. On the other hand, you can't make the assignment for list3, because the actual list instance could return any Animal, not just those that are Dogs, from (for example) the indexer: "Dog dog = list3[0];" would be legal at compile time, but could in fact fail at run-time. This is why "out" and "in" work well as the keywords for variance, because in general a type can be covariant if the type is only used as an output type, and can be contravariant if the type is only used as an input type. For types like List<T> and IList<T> where the type parameter T is used both as input and output, the type is invariant. Try defining your own custom IMyList<T> interface that's the same as IList<T>, but with some kind of variance. You'll find it doesn't work. That's why Microsoft didn't making IList<T> variant in .NET 4.0. :)Anonymous
March 04, 2010
@Alexandra Rusina: "If we had variance on classes, we'd get exactly the same problem as with arrays. It would not be safe." Actually variance on classes shouldn't be a problem. The List<T> class is invariant, and so wouldn't be variant. But other classes could be. I think to some extent the issue is that variances for types would have been extra work, for the language, the compiler, the run-time, etc. At the same time, most classes could not possibly be variant, because the type parameter is used for input and output. So, while they could have allowed variance for classes, the usefulness probably wasn't sufficient to justify the cost. Especially given that if you find yourself in need of some kind of variance between actual classes, you usually would be able to define your own variant interface and have the types implement the interface to allow that use case. Of course, to know for sure why they didn't include that feature, I guess you'd have to ask Eric. :)Anonymous
March 04, 2010
Thank you for puting some ligth in this intricate topic!Anonymous
March 07, 2010
Thanks alot. Very useful and simple.Anonymous
March 08, 2010
Awesome explanation. Thanks for taking the time.Anonymous
March 09, 2010
This makes sense to me. Contravariance for parameters and covariance for return values. I'll have to play around with ref/out parameters. I wasn't aware of 2.0's delegate variance until now; that's good to know! I am happy that only read-only interfaces support covariance. One less opportunity for bugs.Anonymous
March 09, 2010
Wouldn't it be possible to define IList<T> as a specialization of IList<in Tin, out Tout>? Or am I stretching C# into C++ land? :-)Anonymous
March 09, 2010
"Wouldn't it be possible to define IList<T> as a specialization of IList<in Tin, out Tout>?" You could define an IList<in TIn, out TOut>, minus the indexer property (which is read/write in IList<T>). But it seems to me the usefulness of such an interface is dubious. For reading, we'll already have the covariant IEnumerable<out T> implemented by IList<T>, which is half the battle. So that leaves writing; but does that really come up that much? I'm having a hard time imagining a practical scenario where IList<in TIn, out TOut> actually makes the code better. And of course, if by "as a specialization of", you mean that IList<T> would implement IList<in TIn, out TOut>, well since we already have a definition of IList<T> in .NET, I don't see how that can be added without breaking all the existing code that currently implements IList<T>.Anonymous
March 11, 2010
@pete.d You are right about IList<T>. I even deleted my original comment, since yours is more relevant. As for variance on classes - just curios what scenarios do you have in mind? I see that the most often request is variance for collections, and it is not safe, unless collection is read-only. @James I am not sure what you mean here. I am not an expert in C++. You can create IMyList<in Tin, out TOut>, the syntax is supported. But what exactly do you want to achieve here?Anonymous
March 14, 2010
@Alexandra Rusina: "As for variance on classes - just curios what scenarios do you have in mind?" Sorry if I wasn't clear. Your comment is exactly my point. The likelihood of having a class that actually can be variant is low IMHO. In fact, even a read-only collection has to get the data into it somehow (e.g. the constructor). A variant class is certainly theoretically possible. A read-only one that creates output from scratch, or accepts input from some method that's not using the generic type, for example. Likewise a write-only class that only sends data somewhere, never to be seen again. But even if one assumes that there are valid scenarios where a variant class might be useful, surely these scenarios are exceedingly rare, and can be addressed using interfaces anyway, and thus probably do not justify the cost in adding such a feature to the language. (I take it for granted that we all remember, a new language feature has to be more than just useful in some way. It has to be enough useful to justify the cost involved in implementing it).Anonymous
March 18, 2010
I think I may be missing something, I do not think this is correct, as the SetObject returns void, and is not called with anything? Can you clarify what is going on here? // Assume that I have this method: // static void SetObject(object o) { } Action<object> actObject = SetObject;Anonymous
March 19, 2010
@Shawn Yes, SetObject doesn't return anything but sets some value somewhere based on the "object o" parameter. This is why I can initialize Action<object> delegate with this method. Action<delegate> represents a method that returns void and has one parameter of type "object". I don't call a method here, only initialize a delegate with it, this is why I don't pass any parameters in this example. Does this answer your question?Anonymous
April 06, 2010
Great article. I spotted another minor typo. "A couple important rules to remember:" Should read: "A couple of important rules to remember:" (note the insertion of the word "of" between couple and important. :-) )Anonymous
April 09, 2010
@Jason Fixed. Thanks.Anonymous
May 01, 2010
Isnt it just hidden casting? IEnumerable<Object> objects = (IEnumerable<Object>) new List<String>();Anonymous
May 02, 2010
@admg In c#3.5 your statement would not compile. List<string> implements IEnumerable<string> which can not be cast to IEnumerable<object>. C#4.0 allows you to cast IEnumerable<string> to IEnumerable<object>.Anonymous
May 03, 2010
@admg The problems that C# had prior to version 4.0 due to the lack of variance support in generics are well-described here: http://msdn.microsoft.com/en-us/library/ms228359%28VS.90%29.aspx Note that this document is for C# 3.0, because now you should use variance in interfaces instead.Anonymous
June 11, 2010
I had translate this article to chinese. 我已将此文章翻译成中文: www.cnblogs.com/.../covariance-and-contravariance-faq.htmlAnonymous
October 17, 2010
@Alexandra Rusina: I would be glad if out-Parameters could be declared as covariant. Currently it seems as if out and ref Parameters are both treated invariant. Consider the bool TryGetValue(TKey key, out TValue value) Methods of IDictionary: Here the Type 'value' should be declared as covariant. Currently I work around by switching both Return Values: TValue TryGetValue(TKey key, out bool isValid) But using this Signature is not as appealing AND the Parameter Type cannot be derived from a Return Value. Will this be supported in the foreseeable Future?Anonymous
February 24, 2011
Rusina i could't understand what does this mean "// An object of a more derived type is assigned to an object of a less derived type."? what u mean more derived type and less derived type? which is more derived type and which is less derived type? i am waiting eagerly for this answer so that i can learn this variance :( pls hlpAnonymous
January 21, 2012
Object is less derived whereas any other type derived from object such as int or strings or such are more derived.Anonymous
April 07, 2012
May be I am not right, but the first lines of code demonstrates the boxing not assigning of a more derived type to an object of a less derived type. Please correct me if I am wrong. string str = "test"; // An object of a more derived type is assigned to an object of a less derived type. object obj = str;Anonymous
April 07, 2012
Sorry, string is not a value type, then it is not a boxing.Anonymous
October 03, 2012
Nice article, I understood the concept now after reading this articleAnonymous
July 29, 2013
// Contravariance. // Assume that I have this method: // static void SetObject(object o) { } Action<object> actObject = SetObject ??? How can you call a method that does not return anything on an assignment? And where is the parameter 'o'?Anonymous
September 27, 2013
@Sta: A delegate is being assigned; there is no call to SetObject. SetObject(obj); would be a call that would have no return type. Action<object> actObject = SetObject; assigns a delegate to SetObject to actObject. So, now you can call actObject(obj) and it will process SetObject(obj);. Read up on delegates.Anonymous
December 23, 2013
please I want executable example as soon as possibleAnonymous
February 09, 2015
I think the following is wrong, please correct it. It should have been Action<String> // Assume that I have this method: // static void SetObject(object o) { } Action<object> actObject = SetObject;