Contravariance/Covariance, why can't cast this? - c#

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.

Related

Why type-safe is one the major benefits of using generics in C#?

When I was learning C# generics, some articles mentioned using generics is type-safe during execution by preventing the usage of data whose type is different from the one used in the declaration.
Link
I dont get why this should be an issue, if type is wrong shouldn't it crashed when build?
I'm curious about when and how this kind of problem could happen.
I'm curious about when and how this kind of problem could happen.
Basically, when using types that don't support generics but could. The classic example is probably ArrayList - the pre-generics equivalent of List<T>. ArrayList.Add just accepts object, so you can add anything into it - but typically code assumes just a specific type. So for example:
var people = new ArrayList();
people.Add(new Person("Jon"));
// ... later in the code
foreach (string name in people)
{
Console.WriteLine(name);
}
That crashes with a ClassCastException, because the list contains a Person reference, which is then implicitly cast to a string reference. That wouldn't happen with generics, where you'd have either a List<Person> or a List<string>, and spot errors at compile-time.
Generics are indeed type safe in compile time. I'd say that article in the sentence:
Client code that uses generics is type-safe during execution by
preventing the usage of data whose type is different from the one used
in the declaration
is referring to the implicit benefit of eliminating runtime invalid cast exceptions.
Generics provide type safety during compile-time, meaning you can't compile your code if the generic constraint is violated. And that is almost always preferable over a runtime exception.
void DoSomething<T>(T foo) where T : FooBase { }
If I try now to write code like this:
var myBar = new Bar(); // Does not inherit from FooBase
DoSomething(myBar);
Then I get this:
error CS0311: The type 'Bar' cannot be used as type parameter 'T' in the generic type or method 'DoSomething(T)'. There is no implicit reference conversion from 'Bar' to 'FooBase'.
And this happens during compile time. Perfect.
You might have also seen generics without any constraint:
void DomSomething<T>(T foo);
This will go a bit away from your original question, but one could ask what is the benefit over let's say: DoSomething(object obj). And here we have a difference between value types and reference types - namely boxing and unboxing happens when using the version with object.
So generics can also have some performance benefits, next to the type safety and reusability aspect.
One of the major benefits of generics in not just type-safety as is, but allowing writing generalized code while still maintaining type-safety and without degrading performance for value-types. For example we can generalize over collection of interface:
public interface IHaveId { int Id {get;}}
public T GetOrAddById<T>(IList<T> col, int id, T val) where T : class, IHaveId
{
var item = col.FirstOrDefault(x => x.Id == id);
if (item == null)
{
item = val;
col.Add(item);
}
return item;
}
Now you can't pass anything that does not implement the concrete interface.
Before the generics the only way of having generalized collection would be something like ArrayList (which can be compared to List<object>), so user could put anything in it without any type-safety.

Covariance with C# Generics

Given an interface IQuestion and an implementation of that interface AMQuestion, suppose the following example:
List<AMQuestion> typed = new List<AMQuestion>();
IList<IQuestion> nonTyped = typed;
This example yields, as expected, a compile error saying the two are not of the same type. But it states an explicit conversion exists. So I change it to look like this:
List<AMQuestion> typed = new List<AMQuestion>();
IList<IQuestion> nonTyped = typed as IList<IQuestion>;
Which then compiles but, at run time, nonTyped is always null. If someone could explain two things:
Why this doesn't work.
How I can achieve the desired effect.
It would be greatly appreciated. Thank you!
The fact that AMQuestion implements the IQuestion interface does not translate into List<AMQuestion> deriving from List<IQuestion>.
Because this cast is illegal, your as operator returns null.
You must cast each item individually as such:
IList<IQuestion> nonTyped = typed.Cast<IQuestion>().ToList();
Regarding your comment, consider the following code, with the usual cliché animal examples:
//Lizard and Donkey inherit from Animal
List<Lizard> lizards = new List<Lizard> { new Lizard() };
List<Donkey> donkeys = new List<Donkey> { new Donkey() };
List<Animal> animals = lizards as List<Animal>; //let's pretend this doesn't return null
animals.Add(new Donkey()); //Reality unravels!
if we were allowed to cast List<Lizard> to a List<Animal>, then we could theoretically add a new Donkey to that list, which would break inheritance.
Why it doesn't work: as returns null if the value's dynamic type cannot be casted to the target type, and List<AMQuestion> cannot be casted to IList<IQuestion>.
But why can't it? Well, check it:
List<AMQuestion> typed = new List<AMQuestion>();
IList<IQuestion> nonTyped = typed as IList<IQuestion>;
nonTyped.Add(new OTQuestion());
AMQuestion whaaaat = typed[0];
IList<IQuestion> says "You can add any IQuestion to me". But that's a promise it couldn't keep if it were a List<AMQuestion>.
Now, if you didn't want to add anything, just view it as a collection of IQuestion-compatible things, then the best thing to do would be to cast it to an IReadOnlyList<IQuestion> with List.AsReadOnly. Since a read-only list can't have strange things added to it, it can be casted properly.
The issue is that List<AMQuestion> cannot be cast to IList<IQuestion>, so using the as operator does not help. Explicit conversion in this case means to cast AMQuestion to IQuestion:
IList<IQuestion> nonTyped = typed.Cast<IQuestion>.ToList();
By the way, you have the term "Covariance" in your title. In IList the type is not covariant. This is exactly why the cast does not exist. The reason is that the IList interface has T in some parameteres and in some return values, so neither in nor out can be used for T. (#Sneftel has a nice example to show why this cast is not allowed.)
If you only need to read from the list, you can use IEnumerable instead:
IEnumerable<IQuestion> = typed;
This will work because IEnumerable<out T> has out defined, since you can't pass it a T as parameter. You should usually make the weakest "promise" possible in your code to keep it extensible.
IList<T> is not covariant for T; it can't be, as the interface defines functions that take values of type T in an "input" position. However, IEnumerable<T> is covariant for T. If you can limit your type to IEnumerable<T>, you can do this:
List<AMQuestion> typed = new List<AMQuestion>();
IEnumerable<IQuestion> nonTyped = typed;
This does not do any conversions on the list.
The reason you cannot convert a List<AMQuestion> to a List<IQuestion> (assuming AMQuestion implements the interface) is that there would have to be several runtime checks on functions like List<T>.Add, to make sure you were really adding an AMQuestion.
The "as" operator will always return null there as no valid cast exists - this is defined behavior. You have to convert or cast the list like this:
IList<IQuestion> nonTyped = typed.Cast<IQuestion>().ToList();
A type with a generic type parameter can only be covariant if this generic type occurs only in read accesses and contravariant, if it occurs only in write accesses. IList<T> allows both, read and write access to values of type T, so it cannot be variant!
Let's assume that you were allowed to assign a List<AMQuestion> to a variable of type IList<IQuestion>. Now let’s implement a class XYQuestion : IQuestion and insert a value of that type into our IList<IQuestion>, which seems perfectly legal. This list still references a List<AMQuestion>, but we cannot insert a XYQuestion into a List<AMQuestion>! Therefore the two list types are not assignment compatible.
IList<IQuestion> list = new List<AMQuestion>(); // Not allowed!
list.Add(new XYQuestion()); // Uuups!
Because List<T> is not a sealed class, it would be possible for a type to exist which would inherit from List<AMQuestion> and implement IList<IQuestion>. Unless you implement such a type yourself, it's extremely unlikely that one will ever actually exist. Nonetheless, it would be perfectly legitimate to say, e.g.
class SillyList : List<AMQuestion>, IList<IQuestion> { ... }
and explicitly implement all the type-specific members of IList<IQuestion>. It would thus also be perfectly legitimate to say "If this variable holds a reference to an instance of a type derived from List<AMQuestion>, and if that instance's type also implements IList<IQuestion>, convert the reference to the latter type.

Casting constrained generic class in C#

Quite simply, why does this code fail to compile?
public interface IWorld { }
public class Foo<T> where T : IWorld { }
public void Hello<T>(T t) where T : IWorld
{
Foo<IWorld> bar1 = new Foo<T>(); //fails implicit cast
Foo<IWorld> bar2 = (Foo<IWorld>)new Foo<T>(); //fails explicit cast
}
Since every T implements IWorld, every instance of Foo<T> should match Foo<IWorld>. Why not? Is there any way around this? I really don't want to resort to generics to accomplish this.
T : IWorld
means that T has been implemented IWorld and does not mean that it ONLY has implemented IWorld and EXACTLY is IWorld. It may also has been implemented other interfaces.
However, C# supports this cast in it's later versions. Please see http://msdn.microsoft.com/en-us/library/dd799517.aspx (Covariance and Contravariance in Generics)
You can cast to object first
Foo<IWorld> bar2 = (Foo<IWorld>)(object)new Foo<T>();
An even simpler objection - imagine that instead of Foo, this was, say List.
Having converted your List<T> to a List<IWorld>, I can now add some other IWorld implementing object (say of type T2) to a list that is constrained to only contain objects of type T. That shouldn't be valid.
So back to your Foo object - if it contains any methods that expect to be called with objects of type T, I can now call them with any object that implements IWorld - even if (imagine an additional type constraint of Foo) that object would not be an eligible type for Foo.
My point in the comments re: value types. Again, this may be easier if we talk in terms of List<T> - a List<T> for value types contains the value types without boxing. If you want a List<IWorld> of these same values, each value has to be boxed before it's added to the list.
What is the problem with following
Foo<IWorld> bar1 = new Foo<IWorld>();
What are you trying to achieve?
If you need to pass IWorld instance, you can safely pass T, but that is not the case in your code.
EDIT (Based on comments)
To cast to Foo<Array of something> you can use Cast or OfType depending on your requirement(whether you want to throw or ignore incompatible matches).
If it is .NET 4, it should work automatically due to CoVariance feature.

Generic type casting

If i have a type and an object eg.:
- Type someType (coming from somewhere, could be any class eg. MyClass.GetType())
- Object someObject (eg. List<MyClass>())
and want to cast the object back to List<MyClass>. How should i do this?
You can't do this. Generics ensure compile-time safety. You cannot have compile time safety because you know the actual type only at runtime.
You have a runtime type and you want to perform a compile time cast. This is not possible. It is also not clear why you would want to do this in the first place. If you are interested in cases that require reflection, perhaps you should investigate that topic further.
There is no way to have compile-time typing a variable when you only receive the Type information at runtime.
This is different from generics since in generics you get the type information at compile time:
void MyFunc<T>(T thing)
{
// T is passed in at compile time
}
In your case you are getting the type at runtime. So while you can't cast the member to the type the way you normally would you can reflect on the instance and call its members:
void MyFunc(object thing, Type type)
{
var res = t.GetMethod("Add").Invoke(a, new []{"someArg"});
}
Casting means explicitly specifying the type you want to convert to. Since you don't know what your type is, you can't cast to it.
That doesn't mean you can't access the list. If you know the object you have is a list of something, you can cast it to the non-generic IList interface, which provides most of the methods and properties you need:
object obj = GetMyList();
IList list = (IList)obj;
object fifthItem = list[4];
list.RemoveAt(list.Count - 1);
If you describe the problem you're trying to solve rather than the solution you are trying to achieve, then more fitting solutions might be posted.
If you are trying to cast a runtime type at compile time, it is impossible as may said before me.
However, you could cheat a little (but don't use this technique excessively, it leads down a dark road...)
public void DoSomething<T>(List<T> object) where T : ....
{
//do what you want here
}
public void CallIt(Type t, object o) //o is List<Foo>
{
this.GetType().GetMethod("DoSomething").MakeGenericMethod(t).Invoke(o);
}
However I don't see any real benefit to this, as if you don't write any type constraint you gain nothing with using generics instead of objects and IList interface, and if you write any baseclass or interface there, you could just cast your object to that. (For example if you know that T implements IFoo, you could cast o to IList<IFoo> and have every benefit of List<Foo>...

Implicit contravariance on classes - simple failed example

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);

Categories