There are some great resources on covariance and contravariance here on StackOverflow, but I seem to misunderstand the fundamentals of contravariance. I expect this example to work:
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
A a = new B();
B b = new A();
}
}
public class A
{
int id { get; set; }
}
public class B : A
{
}
Setting a to B works, which is covariance, but setting b to a new A fails with a compile error. Even doing an explicit cast still generates error at compile time. Is there a way to do this or do I just completely misunderstand contravariance?
do I just completely misunderstand contravariance?
Yes. You have completely and totally misunderstood what "covariance" and "contravariance" mean. You have confused them with assignment compatibility.
This is an extremely common error. The two concepts are related, but they are not at all the same.
Assignment compatibility is the property that an expression of one type may be stored in a variable of another type.
Covariance is the property that a mapping from types to types preserves the direction assignment compatibility. If Giraffe is assignment compatible with Animal, and this implies that IEnumerable<Giraffe> is assignment compatible with IEnumerable<Animal> then the IEnumerable<T> mapping is covariant.
See my article on the subject for more details:
http://blogs.msdn.com/b/ericlippert/archive/2009/11/30/what-s-the-difference-between-covariance-and-assignment-compatibility.aspx
Setting a to B works, which is covariance
Yes it works. "a" is assignment compatible with B. It is not "covariance" because nothing is varying. There is no mapping from types to types that preserves the direction of assignment compatibility used in the assignment "a = B"; nothing is even generic.
but setting b to a new A fails with a compile error.
Correct.
Is there a way to do this ?
No. Instead of "A" and "B", call them Animal and Giraffe. Every Giraffe is an Animal, so if a variable can hold an Animal, then it can hold a Giraffe. If you try to go the other way, you can't put an Animal into a variable of type Giraffe. The Animal might actually be a Tiger. Why should you be allowed to put a Tiger into a variable of type Giraffe?
You can't do:
B b = new A();
Because an A simply isn't a B. The assignment is invalid. I'm not sure I'd even call this variance - it is simply inheritance.
In the general case where B had members that A doesn't, you can see that it makes no sense to do (if b actually holds a reference to an A object):
b.SomeMethod();
where SomeMethod is defined only in B, but this logic extends to the assignment to the variable itself. Even if you added a cast, the cast has an implicit type check that would fail.
Contravariance is not about violating casting rules; it applies in a situation like:
void WithApple(Action<Apple> eat);
Now, suppose you have some method that knows how to eat any kind of fruit:
void EatFruit(Fruit fruit);
Since it can eat any kind of fruit, you should be able to say:
WithApple(EatFruit);
This was not possible before contravariance support, as the delegate had to match exactly.
This is distinct from covariance, which is the slightly more intuitive notion of:
void EatAllFruit(IEnumerable<Fruit> inBasket);
where you should be able to:
IEnumerable<Apple> apples = someBasket;
EatAllFruit(apples);
Related
I am looking at C# code taken from MSDN documentation on casting.
In this code Animal is a base class and Giraffe is a derived class of Animal class.
Question: Will implicit and explicit casting on reference types create new Heap storage locations? For example, will variable a point to same Heap location that the variable g points to, OR a will point to a new location on Heap that gets created as a result of implicit casting, and what is the reasoning behind the answer?
My guess is NO because then there would be too much Heap memory being consumed by C# code that is doing implicit casting very often. But, I am not sure of the answer and it's reason.
Also, for the explicit casting in same code it seems g2 will point to same location as g and so no new Heap location gets created by successful explicit casting operation on reference types.
Casting operation C# code from MSDN
// Create a new derived type.
Giraffe g = new Giraffe();
// Implicit conversion to base type is safe.
Animal a = g;
// Explicit conversion is required to cast back
// to derived type. Note: This will compile but will
// throw an exception at run time if the right-side
// object is not in fact a Giraffe.
Giraffe g2 = (Giraffe) a;
Animal and Giraffe class C# code
class Animal
{
bool IsFourLegged
{
get;
set;
}
bool CanSpeak
{
get;
set;
}
}
class Giraffe : Animal
{
string Country
{
get;
set;
}
string StripeColor
{
get;
set;
}
}
No, it will not create a new managed object instance. It will just create a new reference (on the call stack, or maybe in a CPU register) to the same instance.
Of course I am talking about casts between two reference types (where one is a base class of the other, or where at least one is an interface type), called reference conversions by the C# spec. Other kinds of casts are different.
As pointed out in the comments, a user-defined conversion operator may also exist between two class types (only when neither is a base class of the other). Such a cast is not a reference conversion, of course, and a different instance (or null) is returned! Commenter's example:
string str = "Name";
XName name = str;
The last line creates a new object instance of type XName (or re-uses an existing XName instance it may have). In any case, an XName can never be the same instance as a string! This conversion could really do anything because it is really just a method call (as seen by the runtime), to the member public static implicit operator XName(string expandedName), actually named op_Implicit or similar within the CIL.
Short version: Object.ReferenceEquals will show that the cast does not create a new object, just another reference to the same object.
Update: I originally thought this might be possible by implementing an explicit conversion if you wanted to make it happen for some reason, but it is not -- the code won't compile. The error message is:
CS0553: User-defined conversion `Giraffe.explicit operator
Animal(Giraffe)' cannot convert to or from a base class
https://ideone.com/G1k7Q2
Also see https://stackoverflow.com/a/17459267/234954 which references the c# spec...
Let's face it, I am still having some difficulties to understand the constrains when it's time to use covariance and contravariance in generics.
I wonder, why if I have this:
public interface IFasterListCov<out T>
{}
public interface IFasterListCon<in T>
{}
public class FasterList<T> : IList<T>, IFasterListCov<T>, IFasterListCon<T>
The third cast fails:
public void QueryNodes<T>() where T:INode
{
//somehow I can convert IFasterListCon<INode> to IFasterListCon<T>
IFasterListCon<INode> nodes = (IFasterListCon<INode>)_nodesDB[type];
//I guess this works because _nodesDB[type] is indeed a FasterList<T> object
//note: I am wrong, I can cast whatever INode implementation, not just T, which made me very confused :P
IFasterListCon<T> nodesT = (IFasterListCon<T>)nodes;
//I can't cast IFasterListCon<T> back to FasterList<T>
FasterList<T> nodeI = nodesT as FasterList<T>; //null
}
Dictionary<Type, IFasterListCov<INode>> _nodesDB;
to be clear _nodesDB[type] is a FasterList<T> declared through IFasterListCov<INode>
MyType : IMyType does not make Generic<IMyType> and Generic<MyType> related in any way.
In your particular case it is likely that nodesT is FasterList<Node> which is not FasterList<INode>.
Note that this conversion work for interface which support variance (co/contra) when you can specify in/out as you see in successful conversion to interface. See one of many questions for details - i.e. Generic Class Covariance.
There is also excellent answer about List co-variance - C# variance problem: Assigning List<Derived> as List<Base> which shows that List<Derived> and List<Base> can't be cast between each other:
List<Giraffes> giraffes = new List<Giraffes>();
List<Animals> animals = new List<Animals>() {new Lion()};
(giraffes as List<Animals>).Add(new Lion()); // What? Lion added to Girafes
Giraffe g = (animals as List<Giraffes>)[0] ; // What? Lion here?
In the scenario where you're calling QueryNodes<MyNode>, in order for your last cast to get a non-null value, the actual instance that you get with _nodesDB[type] must be a FasterList<MyNode>. It's not good enough for it to be FasterList<SomeOtherMostlyCompatibleNode>.
The runtime is very strict about types, it keeps track of the actual runtime types of everything involved, it's not good enough for the data types to be similar, or for you to only have MyNode objects populating your FasterList<SomeOtherMostlyCompatibleNode>, or anything else. If the types are not exactly what they should be, you need to do some sort of programmatic conversion, not just cast.
I stumbled upon a case in where automatic type deduction of the .NET 4.0 MS-C# compiler failed and I had to specify the type "by hand".
This is not a big problem for me, but enough to get me curious why the compiler can not automatically find the correct types for the call..
I reduced the call down to the following programm:
class Program
{
interface GenericInterface<T> { }
interface Covariant<out T> { }
static void Fn<T, U>(GenericInterface<T> t, U u)
where T : Covariant<U>
{
}
class Base { }
class Derived : Base { }
static void Main(string[] args)
{
Base b = null;
Derived d = null;
GenericInterface<Covariant<Base>> c = null;
Fn(c, b); // 1
Fn<Covariant<Base>, Base>(c, d); // 2
Fn(c, d); // 3
}
}
The code does not compile because of the last call to Complex, marked "// 3".
The first call is easy and straight-forward - there is no base/subclass involved. The second call just specifies all parameter "by hand".
I would have expected that the compiler automatically choose the parameter used in the second for the third call as well. Sure, the second parameter is actually given as "Derived" but this can be converted into "Base" and the first parameter needs U to be of type "Base". The where-clause should still be possible with "Base" as U because of the covariant type in the interface.
I don't know exactly the rules for generic type parameter deduction in C#, but I always assumed, it works a bit like "If there is exactly one possible assignment for the parameters, use this one. If not, refuse to compile."
Why doesn't the compiler detect the types automatically? Is this one of these "if a compiler could do this, then it also would have to be able to solve Fermat's Last Theorem" - cases? :D
The where-clause should still be possible with "Base" as U because of the covariant type in the interface.
I think this is where you've gone wrong. The compiler does not consider generic constraints to be part of the method signature.
So I think what might be happening in your third case is that the compiler is deducing T = Covariant<Base> and U = Derived. It then proceeds to check the generic constraint on T. Covariant<Base> doesn't implement Covariant<Derived> so the constraint is not satisfied and compilation fails.
On a few separate occasions, I have tried to coax the declared type out of a variable, relatively far from its declaration, only to find out that typeof(T) only works on type names.
I was wondering if there would be any breaking changes to allow typeof(variable) as well.
For example, with this code:
class Animal { /* ... */ }
class Goat : Animal { /* ... */ }
/* ... */
var g = new Goat();
Animal a = g;
Console.WriteLine(typeof(Goat));
Console.WriteLine(typeof(Animal));
Console.WriteLine(g.GetType());
Console.WriteLine(a.GetType());
You get something like:
Goat
Animal
Goat
Goat
Why is it not possible to do this:
Console.WriteLine(typeof(g));
Console.WriteLine(typeof(a));
Goat
Animal
I have given the spec a cursory glance, and can't find any conflict. I think that it would clear up the question 'Why this type?' when using the typeof operator.
I know that the compiler is capable, here. An implementation using extension methods is actually trivial:
public static Type TypeOf<T>(this T variable)
{
return typeof(T);
}
But that feels dirty, abusing the type-inference of the compiler.
I think the problem here is that .GetType() is older than typeof(). There used to be a day in C# where you needed to do
"0".GetType()
in order to get the String type (for example), until typeof() was born. I think if the concept had been part of the original language design, then indeed it might work as you describe. Due to typeof() being a late introduction to the language, then the designers had to make a choice: Obsolete/deprecate/remove .GetType() (and in the process make many, many uses of it obsolete), make typeof() overlap in functionality with GetType() (which is what you are asking), or make typeof()'s usage not overlap with GetType(). I think the C# people simply chose not to make the functionality overlap (to keep things simple and clear), and so typeof() was restricted in the way it is today.
Suppose it's allowed:
class Animal { }
string Animal;
Type t = typeof(Animal); // uh-oh!
If you are simply trying to get the type of something, you can just call
Type t1 = myObject.GetType();
it should give to the behavior you want.
You seem to be asking two different questions here, the first being why is there no operator for finding the declared variable type, and the other asking why can't typeof(o) be the equivalent of o.GetType().
For the first, the reason is because it serves no purpose, the declared type of a variable is by definition always known at compile time. Adding an operator for it would add no value.
For the other, the problem is that using typeof(instance) causes problems with resolution. As Jason mentioned above, consider:
public class Animal {}
string Animal;
Type t = typeof(Animal);
What is t? If you treat t as typeof(string), then you just adding massive breaking changes to the language. Imagine all the code out there that currently assumes t is an Animal, which is currently correct.
If you treat t as typeof(Animal), though, then your code is incredibly brittle. Imagine the situation where there was no Animal class when you wrote the code, but a year later somebody added an Animal class in some namespace that you imported. Your code would break because the resolution rules would now use the Animal type rather than the local variable.
I have the following code:
var commitmentItems = new List<CommitmentItem<ITransaction>>();
commitmentItems.Add(new CapitalCallCommitmentItem());
And I get the following error:
Argument '1': cannot convert from 'Models.CapitalCallCommitmentItem' to
'Models.CommitmentItem<Models.ITransaction>'
However, CapitalCallCommitmentItem inherits from CommitmentItem<CapitalCall>, and CapitalCall implements ITransaction. So why the error?
Here is a better example:
CapitalCall implements ITransaction
var test = new List<ITransaction>();
test.Add(new CapitalCall());
var test2 = new List<List<ITransaction>>();
test.Add(new List<CapitalCall>()); // error.
Because that would need CommitmentItem<CapitalCall> to be covariant so that it is assignable to CommitmentItem<ITransaction>, which it currently not supported.
C# 4 added support for co- and contravariance in interfaces, but not for classes.
Therefore, if you're using C# 4 and you can use an interface such as ICommitmentItem<> instead of CommitmentItem<>, you might be able to get what you want by using the new features of C# 4.
Let's shorten these names.
C = CapitalCallCommentItem
D = CommitmentItem
E = CapitalCall
I = ITransaction
So your question is that you have:
interface I { }
class D<T>
{
public M(T t) { }
}
class C : D<E> { }
class E : I { }
And your question is "why is this illegal?"
D<E> c = new C(); // legal
D<I> d = c; // illegal
Suppose that was legal and deduce an error. c has a method M which takes an E. Now you say
class F : I { }
Suppose it was legal to assign c to d. Then it would also be legal to call d.M(new F()) because F implements I. But d.M is a method that takes an E, not an F.
Allowing this feature enables you to write programs that compile cleanly and then violate type safety at runtime. The C# language has been carefully designed so that the number of situations in which the type system can be violated at runtime are at a minimum.
Understanding why this doesn't work can be kind of tricky, so here's an analogous example, replacing the classes in your code with some well-known classes from the framework to act as placeholders and (hopefully) illustrate the potential pitfalls of such desired functionality:
// Note: replacing CommitmentItem<T> in your example with ICollection<T>
// and ITransaction with object.
var list = new List<ICollection<object>>();
// If the behavior you wanted were possible, then this should be possible, since:
// 1. List<string> implements ICollection<string>; and
// 2. string inherits from object.
list.Add(new List<string>());
// Now, since list is typed as List<ICollection<object>>, our innerList variable
// should be accessible as an ICollection<object>.
ICollection<object> innerList = list[0];
// But innerList is REALLY a List<string>, so although this SHOULD be
// possible based on innerList's supposed type (ICollection<object>),
// it is NOT legal due to innerList's actual type (List<string>).
// This would constitute undefined behavior.
innerList.Add(new object());
Because "A is subtype of B" does not imply that "X<A> is a subtype of X<B>".
Let me give you an example. Assume that CommitmentItem<T> has a method Commit(T t), and consider the following function:
void DoSomething(CommitmentItem<ITransaction> item) {
item.Commit(new SomethingElseCall());
}
This should work, since SomethingElseCall is a subtype of ITransaction, just like CapitalCall.
Now assume that CommitmentItem<CapitalCall> were a subtype of CommitmentItem<ITransaction>. Then you could do the following:
DoSomething(new CommitmentItem<CapitalCall>());
What would happen? You'd get a type error in the middle of DoSomething, because a SomethingElseCall is passed where a CapitalCall was expected. Thus, CommitmentItem<CapitalCall> is not a subtype of CommitmentItem<ITransaction>.
In Java, this problem can be solved by using the extends and super keywords, cf. question 2575363. Unfortunately, C# lacks such a keyword.
Note - I think Lucero and Eric Lippert himself have provided better direct answers to the question, but I think this is still valuable supplementary material.
Because C# 3.0 doesn't support covariance or contravariance of generic arguments. (And C# 4.0 has limited support for interfaces only.) See here for an explanation of covariance and contravariance, and some insight into the thinking that went on as the C# team were looking at putting this features into C# 4.0:
Covariance and contravariance in C#, part 1
Covariance and Contravariance in C#, Part Two: Array Covariance
Covariance and Contravariance in C#, Part Three: Method Group Conversion Variance
Covariance and Contravariance in C#, Part Four: Real Delegate Variance
Covariance and Contravariance In C#, Part Five: Higher Order Functions Hurt My Brain
Actually, he just keeps writing and writing! Here's everything he's tagged with "covariance and contravariance".