Reputation: 499
I have the following interface
public interface IMyInterface
{
// Some method and property signatures
}
I have the following class implementing the above interface
public class MyClass : IMyInterface
{
// Some methods and properties as declared by IMyInterface
}
Then I have this method in some random class, that I want to return a list of objects implementing IMyInterface
. In this particular implementation these objects are instances of MyClass
.
public List<IMyInterface> getItems(int id)
{
return new List<MyClass>();
}
This will result in a compile error (Visible in real time as well, in Visual Studio)
Cannot implicitly convert type 'System.Collections.Generic.List<MyClass>' to 'System.Collections.Generic.List<IMyInterface>'
I've searched the internet and finally found this thread C# Interfaces and Generic Lists I then ended up with the following method
public List<IMyInterface> getItems(int id)
{
return new List<MyClass>().Cast<IMyInterface>().ToList();
}
This will compile, but to me it seem to be a really odd way to handle it; Casting a concrete class to an Interface. In the thread C# Interfaces and Generic Lists Aequitarum Custos comment to the accepted answer indicate that this should not have to be done.
Am I missing something or is this the way to do it?
Upvotes: 4
Views: 569
Reputation: 68720
Because even though Base
is a subtype of Parent
, doesnt mean List<Base>
is a subtype of List<Parent>
. IList<T>
is invariant in its generic parameter, and so is List<T>
(c# doesn't support class variance, only interface variance).
Read more about covariance, contravariance and invariance here: http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx
If List<T>
was covariant, and you were allowed to do this:
List<Parent> list = new List<Base>();
What would happen if you then did this?
list.Add(new OtherBase());
This would certainly be legal at compile time, but cause a runtime error.
Upvotes: 3
Reputation: 35901
This is because T
parameter in List<T>
is not covariant. But it is in IEnumerable<out T>
:
out T
The type of objects to enumerate.
This type parameter is covariant. That is, you can use either the type you specified or any type that is more derived.
So you can do this, if you'd consider to change signature of getItems
:
public IEnumerable<IMyInterface> getItems(int id)
{
return new List<MyClass>() as IEnumerable<IMyInterface>;
}
You can find more information about covariance and contravariance here.
Upvotes: 2
Reputation: 17808
Consider this example:
public interface IAnimal
{
}
public class Cat : IAnimal
{
}
public class Dog : IAnimal
{
}
And you equivalent method:
public List<IAnimal> getAnimals()
{
return new List<Dog>();
}
// You just Dog'd a list of Cats
IEnumerable<Cat> cats = getAnimals();
Upvotes: 0