Reputation: 1069
Consider the following code:
public class Thing : IThing { }
public interface IThing {}
public interface IContainer<out T> where T : IThing { }
// This works
// public class Container<T> : IContainer<T> where T : IThing { }
// This doesn't work
public class Container<T> : IContainer<IThing> where T : IThing {}
internal class Program
{
private static void Main(string[] args)
{
var concreteContainer = new Container<Thing>();
var abstractContainer = (IContainer<Thing>) concreteContainer;
}
}
On this line:
var abstractContainer = (IContainer<Thing>) concreteContainer;
You get the following runtime error:
InvalidCastException: Unable to cast object of type 'CastTest.Container`1[CastTest.Thing]' to type CastTest.IContainer`1[CastTest.Thing]'.
Also if you have Resharper, it complains with, Suspecious cast: there is no type in the solution which is inherited from both 'Container<Thing>' and 'IContainer<Thing>'
.
Why does there need to be a type that inherits from both? Doesn't Container<T>
implement IContainer<IThing>
? Since Thing
implements IThing
, and T
in Container<T>
is guaranteed to implement IThing
, it seems like I should be able to do this cast.
Upvotes: 0
Views: 40
Reputation:
Doesn't
Container<T>
implementIContainer<IThing>
?
It does.
Since
Thing
implementsIThing
, andT
inContainer<T>
is guaranteed to implementIThing
, it seems like I should be able to do this cast.
out
works the other way around. out
means that if the type implements IContainer<Thing>
, it automatically implements IContainer<IThing>
as well. Not vice versa.
It's called out
because it can return something. You might have for instance
interface IThing<out T> {
T Prop { get; }
}
Now, IContainer<Apple>
would automatically implement IContainer<Fruit>
, and IContainer<Banana>
would also automatically implement IContainer<Fruit>
. That works, because something which returns an Apple
can be interpreted as returning a Fruit
. But if you only know it returns a Fruit
, you don't know whether that Fruit
is an Apple
.
in
works the way you ask. You might have for instance
interface IThing<in T> {
void Act(T t);
}
Now, IContainer<Apple>
does not automatically implement IContainer<Fruit>
. That's because something which requires an Apple
won't be able to accept arbitrary Fruit
s. But something which only requires a Fruit
does accept all Apple
s.
Upvotes: 2