开发者

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!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜