Covariance with C# Generics - c#

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.

Related

Casting object to Dictionary<Guid,AbstractBaseClass> without first casting to Dictionary<Guid,DerivedClass>

If I have an abstract base class called MyBaseClass, and a class deriving from MyBaseClass called MyDerivedClass, is there a way to cast an object that I know is a Dictionary<Guid,MyDerivedClass> to a Dictionary<Guid,MyBaseClass> instead?
I get an invalid cast error when I do:
var dict = (Dictionary<Guid,MyBaseClass>)obj; // Where obj is "object" type but I know is a Dictionary<Guid,MyDerivedClass>.
// This throws an invalid cast error.
The thing that has me confused is that casting an obj that I know is a List<MyDerivedClass> to a List<MyBaseClass> actually works. This works:
var list = (List<MyBaseClass>)obj; // Where obj is "object" type but I know is a List<MyDerivedClass>
// This works
Is there any way to cast an object that is Dictionary<Guid,MyDerivedClass> to Dictionary<Guid,MyBaseClass> without first casting to Dictionary<Guid,MyDerivedClass>? I'd like to avoid have to try casting each derived class if possible.
.NET uses reified generics, so (unlike covariant/contravariant interfaces and arrays) you cannot cast generic classes to have a different signature from what the object actually is. Your example with a List actually doesn't behave the way you say it does:
object obj = new List<MyDerivedClass>();
var list = (List<MyBaseClass>)obj; // invalid cast exception
You can create a new dictionary, though.
var dict = ((Dictionary<Guid, MyDerivedClass>)obj)
.ToDictionary(d => d.Key, d => (MyBaseClass)d.Value);
This will create a copy, and is more expensive than a cast, but for most use cases it won't significantly impact your application's performance.
You could technically create your own class that wraps a differently-typed dictionary and implements an interface like IDictionary<,>, so you could avoid the up-front cost of creating the dictionary but incur slightly more cost as you use the dictionary.
But, in summary, there is no way to simply cast a generic class to a type that it is not.

Contravariance/Covariance, why can't cast this?

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.

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.

Array of a generic class with unspecified type

Is it possible in C# to create an array of unspecified generic types? Something along the lines of this:
ShaderParam<>[] params = new ShaderParam<>[5];
params[0] = new ShaderParam<float>();
Or is this simply not possible due to C#'s strong typing?
It's not possible. In the case where the generic type is under your control, you can create a non-generic base type, e.g.
ShaderParam[] params = new ShaderParam[5]; // Note no generics
params[0] = new ShaderParam<float>(); // If ShaderParam<T> extends ShaderParam
My guess is that this is an XNA type you have no control over though.
You could use a covariant interface for ShaderParam<T>:
interface IShaderParam<out T> { ... }
class ShaderParam<T> : IShaderParam<T> { ... }
Usage:
IShaderParam<object>[] parameters = new IShaderParam<object>[5];
parameters[0] = new ShaderParam<string>(); // <- note the string!
But you can't use it with value types like float in your example. Covariance is only valid with reference types (like string in my example). You also can't use it if the type parameter appears in contravariant positions, e.g. as method parameters. Still, it might be good to know about this technique.
Rather late to the game, but here's how: http://www.codeproject.com/Articles/1097830/Down-the-Rabbit-Hole-with-Array-of-Generics
That wouldn't make sense.
What would happen if you write params[3]?
What type would it be?
You may want to create a non-generic base class or interface and use an array of them.
This is possible in a generic class or a generic extension method where T is defined:
ShaderParam<T>[] params = new ShaderParam<T>[5];
No, the concept ShaderParam<> is meaningless as far as an instantiated type is concerned. In other words, a concrete ShaderParam<float> is not an instance of ShaderParam<>. Therefore, the declared type of the array would be illegal for holding that instance. (Above and beyond the fact that it's already illegal syntax to begin with.)
create an array of unspecified generic types:
Object[] ArrayOfObjects = new Object[5];
ArrayOfObjects[0] = 1.1;
ArrayOfObjects[1] = "Hello,I am an Object";
ArrayOfObjects[2] = new DateTime(1000000);
System.Console.WriteLine(ArrayOfObjects[0]);
System.Console.WriteLine(ArrayOfObjects[1]);
System.Console.WriteLine(ArrayOfObjects[2]);
If you need to perform some type specific actions on the object you could use Reflection
Hope this helps.
Not directly. A closed generic is a specific "type" behind the scenes, so even an open generic is too general to be a "strong type".
The best solution I can think of, which I've used before, is to create a non-generic ancestor to the generic class. Whether that solution will function depends on what you plan to use the array for.

Covariance and Contravariance on the same type argument

The C# spec states that an argument type cannot be both covariant and contravariant at the same time.
This is apparent when creating a covariant or contravariant interface you decorate your type parameters with "out" or "in" respectively. There is not option that allows both at the same time ("outin").
Is this limitation simply a language specific constraint or are there deeper, more fundamental reasons based in category theory that would make you not want your type to be both covariant and contravariant?
Edit:
My understanding was that arrays were actually both covariant and contravariant.
public class Pet{}
public class Cat : Pet{}
public class Siamese : Cat{}
Cat[] cats = new Cat[10];
Pet[] pets = new Pet[10];
Siamese[] siameseCats = new Siamese[10];
//Cat array is covariant
pets = cats;
//Cat array is also contravariant since it accepts conversions from wider types
cats = siameseCats;
As others have said, it is logically inconsistent for a generic type to be both covariant and contravariant. There are some excellent answers here so far, but let me add two more.
First off, read my article on the subject of variance "validity":
http://blogs.msdn.com/b/ericlippert/archive/2009/12/03/exact-rules-for-variance-validity.aspx
By definition, if a type is "covariantly valid" then it is not usable in a contravariant way. If it is "contravariantly valid" then it is not usable in a covariant way. Something that is both covariantly valid and contravariantly valid is not usable in either a covariant or contravariant way. That is, it is invariant. So, there is the union of covariant and contravariant: their union is invariant.
Second, let's suppose for a moment that you got your wish and that there was a type annotation that worked the way I think you want:
interface IBurger<in and out T> {}
Suppose you have an IBurger<string>. Because it is covariant, that is convertible to IBurger<object>. Because it is contravariant, that is in turn convertible to IBurger<Exception>, even though "string" and "Exception" have nothing whatsoever in common. Basically "in and out" means that IBurger<T1> is convertible to any type IBurger<T2> for any two reference types T1 and T2. How is that useful? What would you do with such a feature? Suppose you have an IBurger<Exception>, but the object is actually an IBurger<string>. What could you do with that, that both takes advantage of the fact that the type argument is Exception, and allows that type argument to be a complete lie, because the "real" type argument is an utterly unrelated type?
To answer your follow-up question: implicit reference type conversions involving arrays are covariant; they are not contravariant. Can you explain why you incorrectly believe them to be contravariant?
Covariance and contravariance are mutually exclusive. Your question is like asking if set A can be both a superset of set B and a subset of set B. In order for set A to be both a subset and superset of set B, set A must be equal to set B, so then you would just ask if set A is equal to set B.
In other words, asking for covariance and contravariance on the same argument is like asking for no variance at all (invariance), which is the default. Thus, there's no need for a keyword to specify it.
Covariance is possible for types you never input (e.g. member functions can use it as a return type or out parameter, but never as an input parameter). Contravariance is possible for types you never output (e.g. as an input parameter, but never as a return type or out parameter).
If you made a type parameter both covariant and contravariant, you couldn't input it and you couldn't output it -- you couldn't use it at all.
Without out and in keywords argument is Covariance and Contravariance isn't it?
in means that argument can only be used as function argument type
out means that argument can be used only as return value type
without in and out means that it can be used as argument type and as return value type
Is this limitation simply a language specific constraint or are there deeper, more fundamental reasons based in category theory that would make you not want your type to be both covariant and contravariant?
No, there is a much simpler reason based in basic logic (or just common sense, whichever you prefer): a statement cannot be both true and not true at the same time.
Covariance means S <: T ⇒ G<S> <: G<T> and contravariance means S <: T ⇒ G<T> <: G<S>. It should be pretty obvious that these can never be true at the same time.
What you can do with "Covariant"?
Covariant uses the modifier out, meaning that the type can be an output of a method, but not an input parameter.
Suppose you have these class and interface:
interface ICanOutput<out T> { T getAnInstance(); }
class Outputter<T> : ICanOutput<T>
{
public T getAnInstance() { return someTInstance; }
}
Now suppose you have the types TBig inheiriting TSmall. This means that a TBig instance is always a TSmall instance too; but a TSmall instance is not always a TBig instance. (The names were chosen to be easy to visualize TSmall fitting inside TBig)
When you do this (a classic covariant assignment):
//a real instance that outputs TBig
Outputter<TBig> bigOutputter = new Outputter<TBig>();
//just a view of bigOutputter
ICanOutput<TSmall> smallOutputter = bigOutputter;
bigOutputter.getAnInstance() will return a TBig
And because smallOutputter was assigned with bigOutputter:
internally, smallOutputter.getAnInstance() will return TBig
And TBig can be converted to TSmall
the conversion is done and the output is TSmall.
If it was the contrary (as if it were contravariant):
//a real instance that outputs TSmall
Outputter<TSmall> smallOutputter = new Outputter<TSmall>();
//just a view of smallOutputter
ICanOutput<TBig> bigOutputter = smallOutputter;
smallOutputter.getAnInstance() will return TSmall
And because bigOutputter was assigned with smallOutputter:
internally, bigOutputter.getAnInstance() will return TSmall
But TSmall cannot be converted to TBig!!
This then is not possible.
This is why "contravariant" types cannot be used as output types
What you can do with "Contravariant"?
Following the same idea above, contravariant uses the modifier in, meaning that the type can be an input parameter of a method, but not an output parameter.
Suppose you have these class and interface:
interface ICanInput<in T> { bool isInstanceCool(T instance); }
class Analyser<T> : ICanInput<T>
{
bool isInstanceCool(T instance) { return instance.amICool(); }
}
Again, suppose the types TBig inheriting TSmall. This means that TBig can do everything that TSmall does (it has all TSmall members and more). But TSmall cannot do everything TBig does (TBig has more members).
When you do this (a classic contravariant assignment):
//a real instance that can use TSmall methods
Analyser<TSmall> smallAnalyser = new Analyser<TSmall>();
//this means that TSmall implements amICool
//just a view of smallAnalyser
ICanInput<TBig> bigAnalyser = smallAnalyser;
smallAnalyser.isInstanceCool:
smallAnalyser.isInstanceCool(smallInstance) can use the methods in smallInstance
smallAnalyser.isInstanceCool(bigInstance) can also use the methods (it's looking only at the TSmall part of TBig)
And since bigAnalyser was assigned with smallAnalyer:
it's totally ok to call bigAnalyser.isInstanceCool(bigInstance)
If it was the contrary (as if it were covariant):
//a real instance that can use TBig methods
Analyser<TBig> bigAnalyser = new Analyser<TBig>();
//this means that TBig has amICool, but not necessarily that TSmall has it
//just a view of bigAnalyser
ICanInput<TSmall> smallAnalyser = bigAnalyser;
For bigAnalyser.isInstanceCool:
bigAnalyser.isInstanceCool(bigInstance) can use the methods in bigInstance
but bigAnalyser.isInstanceCool(smallInstance) cannot find TBig methods in TSmall!!! And it's not guaranteed that this smallInstance is even a TBig converted.
And since smallAnalyser was assigned with bigAnalyser:
calling smallAnalyser.isInstanceCool(smallInstance) will try to find TBig methods in the instance
and it may not find the TBig methods, because this smallInstance may not be a TBig instance.
This is why "covariant" types cannot be used as input parameters
Joining both
Now, what happens when you add two "cannots" together?
Cannot this + cannot that = cannot anything
What could you do?
I haven't tested this (yet... I'm thinking if I'll have a reason to do this), but it seems to be ok, provided you know you will have some limitations.
If you have a clear separation of the methods that only output the desired type and methods that only take it as an input parameter, you can implement your class with two interfaces.
One interface using in and having only methods that don't output T
Another interface using out having only methods that don't take T as input
Use each interface at the required situation, but don't try to assign one to another.
Generic type parameters cannot be both covariant and contravariant.
Why? This has to do with the restrictions which in and out modifiers impose. If we wanted to make our generic type parameter both covariant and contravariant, we would basically say:
None of the methods of our interface returns T
None of the methods of our interface accepts T
Which would essentially make our generic interface non-generic.
I explained it in detail under another question:

Categories