Say I have a setup like
public interface IMyInterface { }
public class MyImplementation : IMyInterface { }
public class MyClass<T> where T : IMyInterface { }
I figured out that I can't do a substitution like
var list = new List<MyClass<IMyInterface>>()
{
new MyClass<MyImplementation>()
}
as I'll get the error
Cannot convert from MyClass<MyImplementation>
to
MyClass<IMyInterface>
Does that mean what I'm trying to do is code smell?
It does not allow that because the T
in List<T>
is invariant. Here is an example which will explain:
public interface IFruit
{
string Name { get; set; }
}
public class Apple : IFruit
{
public string Name { get; set; }
public string Color { get; set; }
}
public class Orange : IFruit
{
public string Name { get; set; }
}
Let's add some fruits to a list:
var fruits = new List<IFruit>();
fruits.Add(new Apple());
fruits.Add(new Orange());
Later in the code, you have no idea what was added and you do this:
Orange orange = fruits[0];
Now that will not compile but it also does not make sense because every IFruit
is not an Apple
or an Orange
.
What if the compiler allowed it?
If, for instance let's say, what you are trying was allowed by the compiler, like this:
// does not compile but let's say it did
List<Apple> fruits = new List<IFruit>();
Then if the compiler allowed that, it should also allow this since they both implement IFruit
:
fruits.Add(new Apple());
fruits.Add(new Orange());
then later on somewhere in code you do this (since it is a list of apples):
foreach (var thisFruit in fruits)
{
Console.WriteLine(thisFruit.Color)
}
Crash!! The compiler did not stop you at the beginning and at this point you are looking at the code and it says List<Apple>
, and you wrote code and all compiled. At runtime, oh crap, there is no Color
property because it is an Orange
.
This is why it is not allowed.