Generic method with variance in C#?
Consider following classes (inheritance tree):
public class A {}
public class B: A {}
And this method:
public IList<A> MyMethod(){
IList<B> result = new List<B>();
//add some items to result
return result;
}
The compiler is unhappy. Error is Cannot convert expression type IList<B> to return type IList<A>
. How do I solve this ? In another words how to specify that MyMethod
will return IList<T>
of T
where T
can be anything that inherits from A
or instances of A开发者_如何学JAVA
itself ?
What you're asking for is impossible because IList<T>
does not support variance -- you cannot use IList<B>
anywhere that is expecting IList<A>
. You'll have to explain more details of what you want in order to come up with a solution.
Possible solutions are:
public IList<A> MyMethod(){
IList<A> result = new List<A>();
//add some items to result
return result;
}
Or
public IEnumerable<A> MyMethod(){
IList<B> result = new List<B>();
//add some items to result
return result;
}
You cannot convert an IList<B> to IList<A>, even if B inherits from A. Otherwise, the user might attempt to add an instance of A that is not B into the list.
public void Example(){
IList<B> listB = new List<B>();
IList<A> listA = listB;
listA.Add(new A()); // Can't insert A into a list of B
}
Can you return IEnumerable<A> instead of IList<A>? IEnumerable<A> is covariant, unlike IList<A>.
how to specify that MyMethod will return IList of T where T can be anything that inherits from A or instances of A itself ?
You don't have to
You can just declare that it returns IList<A>
. Why? Becase - given that B
inherits from A
- every item of B
can be passed where the requiered type is A
.
Call it polymorphism by inheritance, Liskov substitution principle, or method variance, the name doesn't matter. What matters is that the following works (tested on LinqPad):
public class A {}
public class B: A {}
public IList<A> MyMethod()
{
var result = new List<A>();
//add some items to result
result.Add(new B());
return result;
}
Genetic alternatives
In fact, you can tell that you are going to return a IList<TA>
and request a few derived types (TB
, TC
...) to populate it with. That's right, the following example also works (tested on LinqPad):
void Main()
{
MyMethod<A, B, C>();
}
public class A {}
public class B: A {}
public class C: A {}
public IList<TA> MyMethod<TA, TB, TC>()
where TB : TA, new()
where TC : TA, new()
where TA : class
{
var result = new List<TA>();
//add some items to result
result.Add(new B() as TA);
result.Add(new C() as TA);
return result;
}
Or if you want to keep a particular base type (say you want to return an IList<A>
but it actually contains items of classes that derive from A
, then you can do this:
void Main()
{
MyMethod<B, C>();
}
public class A {}
public class B: A {}
public class C: A {}
public IList<A> MyMethod<T1, T2>()
where T1 : A, new()
where T2 : A, new()
{
var result = new List<A>();
//add some items to result
result.Add(new T1() as A);
result.Add(new T2() as A);
return result;
}
You don't have to, but you can
OK, if you really want to say it returns IList<T>
where T : A
. Then say that!
void Main()
{
MyMethod<B>();
}
public class A {}
public class B: A {}
//public class C: A {} //Even if I don't add that class
public IList<T> MyMethod<T>()
where T : A, new()
{
var result = new List<T>();
//add some items to result
result.Add(new T());
return result;
}
Yes, that one cannot return a mix of item of type T
and items of type A
, because it says it returns IList<T>
and not every item of type A
is also an item of type T
.
What happens with your code
Look at your code:
public IList<A> MyMethod(){
IList<B> result = new List<B>();
//add some items to result
return result;
}
You are trying to return an IList<B>
when you said that you was going to return an IList<A>
. Let's suppose that that works... then what would happen to the caller of your method? Let's see:
public class A {}
public class B: A {}
public class C: A {}
void Main()
{
//Hmmm... I need a IList<T>, let's call MyMethod!
IList<A> list = MyMethod();
//Cool, I got an IList<A>, now let's add some items...
var item = new C();
//Well, item is of type C...
// and C inherits from A, so I must be able to add it...
list.Add(item); //BOOM!
//It was actually an IList<B>!
// and C doesn't dervive from B, so you can't add it.
}
DFTBA!
精彩评论