What is the difference between covariance and upcasting, or, more specifically, why are they given different names?
I've seen the following example referred to as 'upcasting':
string s = "hello";
object o = s; //upcast to 'string' to 'object'
Whereas, the following I have seen called 'covariance':
string[] s = new string[100];
object[] o = s;
IEnumerable<string> ies = new List<string>();
IEnumerable<object> ieo = ies;
Now, to my untrained eye, covariance seems to be the same as upcasting, except that it refers the casting of collections. (And of a similar statement can be made regarding contravariance and downcasting).
Is it really that simple?
Now, to my untrained eye, covariance seems to be the same as upcasting, except that it refers the casting of collections. (And of a similar statement can be made regarding contravariance and downcasting).
Is it really that simple?
Covariance isn't about upcasting, although I can see why you think it's related.
Covariance is about the following very simple idea. Let's say you have a variable derivedSequence of type IEnumerable<Derived>. Let's say you have a variable baseSequence of type IEnumerable<Base>. Here, Derived derives from Base. Then, with covariance, the following is a legal assignment, and an implicit reference conversion occurs:
baseSequence = derivedSequence;
Note that this is not upcasting. It is not the case that IEnumerable<Derived> derives from IEnumerable<Base>. Rather, it is covariance that allows you to assign the value of the variable derivedSequence to the variable baseSequence. The idea is that variables of type Base can be assigned from objects of type Derived, and since IEnumerable<T> is covariant in its parameter, objects of type IEnumerable<Derived> can be assigned to variables of type IEnumerable<Base>.
Of course, I haven't yet really explained what covariance is. In general, covariance is about the following simple idea. Let's say you have a mapping F from types to types (I'll denote this mapping by F<T>; given a type T its image under the mapping F is F<T>.) Let's say that this mapping has the following very special property:
if X is assignment compatible with Y, then F<X> is assignment compatible with F<Y> as well.
In this case, we say that F is covariant in its parameter T. (Here, to say that "A is assignment compatible with B" where A and B are reference types means that instances of B can be stored in variables of type A.)
In our case, IEnumerable<T> in C# 4.0, an implicit reference conversion from instances of IEnumerable<Derived> to IEnumerable<Base> if Derived is derived from Base. The direction of assignment compatibility is preserved, and this is why we say that IEnumerable<T> is covariant in its type parameter.
Casting refers to changing the static type of objects and expressions.
Variance refers to the interchangeability or equivalence of types in certain situations (such as parameters, generics, and return types).
IEnumerable<string> is not derived from IEnumerable<object>, so the cast between them is not upcasting. IEnumerable is covariant in its type parameter and string is derived from object, so the cast is allowed.
The reason they are different concepts is that, unlike upcasting, covariance is not always allowed. It would have been easy for the designers of the type-system to make IList<Cat> be considered as "derived" from IList<Animal>, but then we run into problems:
IList<Cat> cats = new List<Cat>();
IList<Animal> animals = cats;
animals.Add(new Dog()); //Uh oh!
If this were allowed, now our cats list would contain a Dog!
In contrast, the IEnumerable<T> interface has no way of adding elements, so this is perfectly valid (in C# 4.0):
IList<Cat> cats = new List<Cat>();
IEnumerable<Animal> animals = cats;
//There's no way to add things to an IEnumerable<Animal>, so here we are ok
Thie blog post below has a good explanation of it:
http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx
From what I can gather covariance removes the need for explicit downcasting subsequent to a previous upcast. Typically if you upcast an object you can only access the base type methods and attributes, with covariance it seems you can imply the downcast by replacing lesser derived types with more derived types in the more derived class declaration.
Related
I am just not getting how to convert or cast List<ISomeImplementation> to List<SomeImplementation>.
// lstOfImplemantation is of type `List<SomeImplementation>`
List<ISomeImplementation> lstOfInterfaces = lstOfImplemantation; // how to convert it.
any help is appreciated.
You can use LINQ in one way:
List<ISomeImplementation> lstOfInterfaces = lstOfImplemantation.Cast<IISomeImplementation>().ToList();
Casting interface to implementation is not good pratice. What if there some other implementations of that interface? You cannot assume that all implemtations are the of type SomeImplementation. If you are sure, use List<SomeImplementation>.
Just because A derives from B, doesn't necessarily mean X<A> derives from X<B>.
In fact, List<T> is invariant in its generic type paramater T. This means List<string> isn't a subtype nor a supertype of List<object>, even though string derives from object.
Imagine if your list of interfaces contained instances of type SomeOtherImplementation. How would the cast work?
Instead, you should create a new list and cast each item in your list of interfaces to the concrete type.
List<SomeImplementation> lstOfImplementation =
lstOfInterfaces.Cast<SomeImplementation>()
.ToList();
If you're not sure whether all instances are of type SomeImplementation, you can filter out those who aren't, using OfType<T>.
List<SomeImplementation> lstOfImplementation =
lstOfInterfaces.OfType<SomeImplementation>()
.ToList();
Read also:
Covariance and Contravariance
Covariance and Contravariance FAQ
This question already has answers here:
Why covariance and contravariance do not support value type
(4 answers)
Closed 8 years ago.
I'm trying to figure out why this doesn't work:
public void DefaultAction( object obj = null ){}
public void Start()
{
SomeReferenceType obj;
DefaultAction( obj ); //works
int i;
string s;
DefaultAction( i ); //works
DefaultAction( s ); //works
}
//however...
public event Action OnNullAction = DefaultAction; //works
public event Action<SomeReferenceType> OnObjectAction = DefaultAction; //works
public event Action<int> OnIntAction = DefaultAction; //doesn't work!!
Trying to bind a void(object) to an Action<ValueType> throws a parameter mismatch error, even though you can call the function directly with an int/string/bool what have you. Is there some mysterious boxing/unboxing happening? And regardless, is it possible to create a delegate that can respond to any Action<T>?
You have discovered that delegate contravariance requires reference types.
That's pretty highfalutin, I know.
First off, let me clearly state what covariance and contravariance are. Suppose you have a relationship between types: "A value of type Giraffe can be assigned to a variable of type Animal". Let's notate that as
Animal <-- Giraffe
A generic type C<T> is said to be covariant in T if replacing every type with C<that type> preserves the direction of the arrow.
IEnumerable<Animal> <-- IEnumerable<Giraffe>
Since C# 4.0, when I added this feature to the language, ou can use a sequence of giraffes anywhere you need a sequence of animals.
A generic type C<T> is said to be contravariant in T if the replacement reverses the direction of the arrow:
Action<Animal> --> Action<Giraffe>
If you need an action that requires that you give it a Giraffe and you have an action that can take any Animal, then you're all set; you need something that can take a Giraffe and an Action<Animal> can take a Giraffe. But this is not covariant. If you have an Action<Giraffe> in hand and you need an Action<Animal>, you can't use the Action<Giraffe> because *you can pass a Tiger to an Action<Animal> but not an Action<Giraffe>.
What about Func<T>? It is covariant in T. If you need a function that returns an Animal and you have a function that returns a Giraffe, you're good, because the Giraffe will be an Animal.
What about Func<A, R>? It is contravariant in A and covariant in R. It should be clear why.
So now that we know what covariance and contravariance of generic types are, what are the rules in C#? The rules are:
The type declarations must be annotated with in (contravariant) and out (covariant). For example, delegate R Func<in A, out R>(A a). Notice that the in is the thing that goes into the function and the out is the thing that comes out of the function; we named them in and out on purpose.
The compiler has to be able to prove that the annotations are safe. See the spec or my blog for details.
Variance is only supported on generic delegates and interfaces, not on generic structs, enums or classes.
The varying types have to both be reference types.
So now we come to your question. Why do they have to both be reference types? You deduced the answer: where is the boxing instruction?
Action<object> oa = (object x)=>whatever;
Action<int> ia = oa; // Suppose this works.
ia(123);
Where is the boxing instruction? Not in the body of the lambda assigned to oa -- that thing takes an object already. Not in the call to ia(123) -- that thing takes an integer. The only possible solution is that oa and ia be unequal; that this be a shorthand for
Action<object> oa = (object x)=>{whatever};
Action<int> ia = (int x)=>{ oa(x); };
But if that's what you meant to say, then just say that. People expect that a reference conversion will maintain referential identity, so C# outlaws covariant or contravariant conversions that would have to box or unbox a value.
If you have more questions on this, search my old blog (blogs.msdn.com/ericlippert) for covariance or search for the C# covariance FAQ.
Just make it generic
public void DefaultAction<T>(T param) { }
Just came across this:
Func<List<object>> foo = () => new List<object>();
List<string> s = (List<string>)foo();
IList<string> s1 = (IList<string>)foo();
Compiler complains about casting to List (makes sense), but says nothing about IList. Makes me wonder why is that?
The compiler knows that a List<X> cannot be a List<Y>.
It therefore gives a compiler error.
However, the second cast could succeed if the List<X> is actually some derived class that also implements IList<Y>.
You will only get a compile-time error from a cast if neither type is an interface, or if one type is an unrelated interface and the other type is sealed (or a struct).
To quote the spec (ยง6.4.2)
The explicit reference conversions are:
From object and dynamic to any other reference-type.
From any class-type S to any class-type T, provided S is a base class of T.
From any class-type S to any interface-type T, provided S is not sealed and provided S does not implement T.
From any interface-type S to any class-type T, provided T is not sealed or provided T implements S.
From any interface-type S to any interface-type T, provided S is not derived from T.
[snip]
(emphasis added)
The provided... clauses exclude conversions that are actually implicit.
An object that is known to be a List<object> might implement IList<string> as well as IList<object>, so it's possible that the cast can succeed. It can't in this case because we know that the statement is simply new List<object>(), but the compiler doesn't consider that. You might've extended List<T> and implemented the other, e.g.
// not recommended, but second cast works
public class MyWeirdList : List<object>, IList<string>
An object that is known to be a List<object> cannot possibly also be a List<string>, because you can only inherit from a single type.
public class MyWeirdList : List<object>, List<string> // compiler error
If List<T> were sealed, both casts would be invalid, because then the compiler would know for sure that the class couldn't implement IList<string>. You can try this by using this class instead:
public sealed class SealedList<T> : List<T> { }
The first line fails at compile time, the second gives an "Unable to cast object of type 'System.Collections.Generic.List1[System.Object]' to type 'System.Collections.Generic.IList1[System.String]'." exception during runtime.
If you look at this question (Cast IList<string> to IList<object> fails at runtime), the answer clarifies why this compile works and also provides an example for a class that could satisfy the conditions provided.
I was reading the Covariance and Contravariance in C#.
According to my understanding,
If we have a class Animal and a derived class Cat then,
Covariance feature makes the compiler accepts passing a Cat type object to Animal type object and Contravariant is vice-versa.
Everything is ok till I read this line.
"To annotate type-covariance you use out parameter and for contravariant you use in parameter"
According to me, a parameter decorated with out keyword => the value must be assigned to that particular parameter in a function.
I have difficulties to associate the out keyword to covariance and in keyword to contravariant.
How are these two related ? Any super simple examples for both?
Thanks in advance.
Both covariance and contravariance in C# 4.0 refer to the ability of using a derived class instead of base class. The in/out keywords are compiler hints to indicate whether or not the type parameters will be used for input and output.
Covariance
Covariance in C# 4.0 is aided by out keyword and it means that a generic type using a derived class of the out type parameter is OK. Hence
IEnumerable<Fruit> fruit = new List<Apple>();
Since Apple is a Fruit, List can be safely used as IEnumerable
Contravariance
Contravariance is the in keyword and it denotes input types, usually in delegates. The principle is the same, it means that the delegate can accept more derived class.
public delegate void Func<in T>(T param);
This means that if we have a Func, it can be converted to Func.
Func<Fruit> fruitFunc = (fruit)=>{};
Func<Apple> appleFunc = fruitFunc;
Why are they called co/contravariance if they are basically the same thing?
Because even though the principle is the same, safe casting from derived to base, when used on the input types, we can safely cast a less derived type (Func) to a more derived type (Func), which makes sense, since any function that takes Fruit, can also take Apple.
out is a contextual keyword, i.e. depending on its placement, it means different things.
You are talking about the parameter modifier. But that's not what it is with regards to Co- and Contravariance. When the keyword is used in a generic interface definition, it is the generic modifier, which is something else completely.
out keyword serves different purposes. As you noted in the context of "generic type parameters" it acts as a keyword to make "covariance" work, another use is similar to ref keyword which we use to get multiple return values from a Method.
If you look at the documentation for out-keyword in MSDN two purposes of out keyword will be listed. Not to be confused it acts different based on the context.
You can find similarities with new keyoword also, it also serves different purposes. AFAIK they are
To create new instance of a class. object obj = new object();
To explicitly shadow base class method or property. protected new void BaseMethod(){}
Generic parameter constraint. private void MyGenericMethod<T>(T t) where T:new()
I think Microsoft guys make this feature very complexe by using not clear names for the feature name and not good keywords for the feature parameters.
I know this name from Covariance and contravariance of vectors (Mathe) and when I hear about it first time in C# I got a shock!
#JoesphAlbahari in his book C# 4 In a Nutshell have explained this topic very well page 109-112. I recommend you really to read it.
I am wondering if dynamic is semantically equivalent to object when used as a generic type parameter. If so, I am curious why this limitation exists since the two are different when assigning values to variables or formal parameters.
I've written a small experiment in C# 4.0 to tease apart some of the details. I defined some simple interfaces and implementations:
interface ICovariance<out T> { T Method(); }
interface IContravariance<in T> { void Method(T argument); }
class Covariance<T> : ICovariance<T>
{
public T Method() { return default(T); }
}
class Contravariance<T> : IContravariance<T>
{
public void Method(T argument) { }
}
The interesting details of the experiment:
class Variance
{
static void Example()
{
ICovariance<object> c1 = new Covariance<string>();
IContravariance<string> c2 = new Contravariance<object>();
ICovariance<dynamic> c3 = new Covariance<string>();
IContravariance<string> c4 = new Contravariance<dynamic>();
ICovariance<object> c5 = new Covariance<dynamic>();
IContravariance<dynamic> c6 = new Contravariance<object>();
// The following statements do not compile.
//ICovariance<string> c7 = new Covariance<dynamic>();
//IContravariance<dynamic> c8 = new Contravariance<string>();
// However, these do.
string s = new Covariance<dynamic>().Method();
new Contravariance<string>().Method((dynamic)s);
}
}
The first two statements with c1 and c2 demonstrate that basic covariance and contravariance are working. I then use c3 and c4 to show that dynamic can be used as a generic type parameter in the same fashion.
The statements with c5 and c6 reveal that a conversion from dynamic to object is always valid. This isn't really too surprising, since object is an ancestor of all other types.
The final experiment with c7 and c8 is where I start to become confused. It implies that methods that return dynamic objects are not substitutes for methods that return string ones, and similarly that methods that accept string objects cannot take dynamic ones. The final two statements with the assignment and method call show this is clearly not the case, hence my confusion.
I thought about this a little, and wondered if this is to prevent programmers from using ICovariance<dynamic> as a stepping stone between type conversions that would result in run-time errors, such as:
ICovariance<dynamic> c9 = new Covariance<Exception>();
ICovariance<string> c10 = c9;
// While this is definitely not allowed:
ICovariance<string> c11 = new Covariance<Exception>();
However, this is unconvincing in the case of dynamic since we lose type-safety anyway:
dynamic v1 = new Exception();
string v2 = v1;
Put another way, the question is "Why does the semantics of dynamic differ between assignment and covariance/contravariance with generics?"
I am wondering if dynamic is semantically equivalent to object when used as a generic type parameter.
Your conjecture is completely correct.
"dynamic" as a type is nothing more than "object" with a funny hat on, a hat that says "rather than doing static type checking for this expression of type object, generate code that does the type checking at runtime". In all other respects, dynamic is just object, end of story.
I am curious why this limitation exists since the two are different when assigning values to variables or formal parameters.
Think about it from the compiler's perspective and then from the IL verifier's perspective.
When you're assigning a value to a variable, the compiler basically says "I need to generate code that does an implicit conversion from a value of such and such a type to the exact type of the variable". The compiler generates code that does that, and the IL verifier verifies its correctness.
That is, the compiler generates:
Frob x = (Frob)whatever;
But limits the conversions to implicit conversions, not explicit conversions.
When the value is dynamic, the compiler basically says "I need to generate code that interrogates this object at runtime, determines its type, starts up the compiler again, and spits out a small chunk of IL that converts whatever this object is to the type of this variable, runs that code, and assigns the result to this variable. And if any of that fails, throw."
That is, the compiler generates the moral equivalent of:
Frob x = MakeMeAConversionFunctionAtRuntime<Frob>((object)whatever);
The verifier doesn't even blink at that. The verifier sees a method that returns a Frob. That method might throw an exception if it is unable to turn "whatever" into a Frob; either way, nothing but a Frob ever gets written into x.
Now think about your covariance situation. From the CLR's perspective, there is no such thing as "dynamic". Everywhere that you have a type argument that is "dynamic", the compiler simply generates "object" as a type argument. "dynamic" is a C# language feature, not a Common Language Runtime feature. If covariance or contravariance on "object" isn't legal, then it isn't legal on "dynamic" either. There's no IL that the compiler can generate to make the CLR's type system work differently.
This then explains why it is that you observe that there is a conversion from, say, List<dynamic> to and from List<object>; the compiler knows that they are the same type. The specification actually calls out that these two types have an identity conversion between them; they are identical types.
Does that all make sense? You seem very interested in the design principles that underly dynamic; rather than trying to deduce them from first principles and experiments yourself, you could save yourself the bother and read Chris Burrows' blog articles on the subject. He did most of the implementation and a fair amount of the design of the feature.
for this one:
ICovariance<string> c7 = new Covariance<dynamic>();
reason is obvious, if it was possible then you could do:
c7.Method().IndexOf(...);
and it will definitely fail, except if dynamic is not string or has those method.
since (even after all the changes) c# is not dynamic language. Covariance is allowed only when it is definitely safe. You can of course shot into your feet and call IndexOf on dynamic variable, but you can't let users of your API to do it unintentionally. For example if you return such a ICovariance<string> with dynamic undercover calling code might fail!
Remember the rule, D is covariant to B if there is a cast from D to B. In this case there is no cast from dynamic to string.
But dynamic is covariant to object just because everything is derived from it.
Because dynamic and covariant/contravariant keywords are so new?
I would guess that you kind of answered your own question. Assignment type-safety is relaxed in assignment statements, because that's how dynamic works; it short-circuits compile-time type-checking so you can make assignments you EXPECT to work from objects the compiler has no clue about.
However, generic covariance/contravariance is rigidly controlled; without the use of the in/out keywords (which were also introduced alongside dynamic in C# 4.0) you couldn't convert either way. The generic parameters, even with co/contravariance allowed, require the types to be in the same branch of the inheritance hierarchy. A String is not a dynamic and a dynamic is not a string (though both are Objects and a dynamic may refer to what could be accessed as a string), so the generic type-checking inherent in the covariance/contravariance checks fails, while OTOH, the compiler is expressly told to ignore most non-generic operations involving dynamic.
"Why does the semantics of dynamic differ between assignment and covariance/contravariance with generics?"
The answer is that when using generics you are abstracted from the data type itself. However, it also implies that generic is generic enough that all types will share the same functionality.
So if you have 'ICovariance c9 = new Covariance();` both dynamic and exception do not have the same functionalities (as base types). More over, the compiler doesn't have a clue as to how to convert from dynamic to exception (even though they both inherit from object).
If there was an explicit inheritance hierarchy between dynamic and Exception (other than object), than this would be somewhat ok.
The reason somewhat is because you can downcast, but not upcast. EG, if exception inherits from dynamic, than it would be fine. If dynamic inherits from Exception it would be an upcast kinda deal and that would not be ok, since there could be the condition where the 'dynamic's data is not present inException`.
.NET has these explicit typecasts built in, and you can see them in action in the System.Convert object. However, types that are super specific cannot be easily implicitly or explicitly casted between one another without custom code. And this is one of the reasons why having multi-types fails (as is the case with 'ICovariance c9 = new Covariance();` ). This is also built to preserve type safety.