Substituting implementations into the T of a param

2019-08-14 05:37发布

问题:

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?

回答1:

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.



标签: c# .net oop