C#中有差异的通用方法?

时间:2011-04-25 00:56:35

标签: c# .net generics collections covariance

考虑以下类(继承树):

public class A {}
public class B: A {}

这个方法:

public IList<A> MyMethod(){
    IList<B> result = new List<B>();

    //add some items to result

    return result;
}

编译器不满意。错误是Cannot convert expression type IList<B> to return type IList<A>。我该如何解决这个问题?换句话说,如何指定MyMethod将返回IList<T> T T,其中A可以是从A继承的任何内容或{{1}}的实例本身?

3 个答案:

答案 0 :(得分:4)

您要求的内容是不可能的,因为IList<T>不支持差异 - 您无法在期望IList<B>的任何地方使用IList<A>。为了提出解决方案,你必须解释你想要的更多细节。

可能的解决方案是:

public IList<A> MyMethod(){
    IList<A> result = new List<A>();

    //add some items to result

    return result;
}

或者

public IEnumerable<A> MyMethod(){
    IList<B> result = new List<B>();

    //add some items to result

    return result;
}

答案 1 :(得分:2)

您无法转换IList&lt; B&gt;到IList&lt; A&gt;,即使B继承自A.否则,用户可能会尝试将不是B的A实例添加到列表中。

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
}

你能否返回IEnumerable&lt; A&gt;而不是IList&lt; A&gt;? IEnumerable的&LT a取代;与IList&lt; A&gt;不同,它是协变的。

答案 2 :(得分:1)

  

如何指定MyMethod将返回T的IList,其中T可以是从A继承的任何东西还是A本身的实例?


您没有

您可以声明它返回IList<A>。为什么?考虑到B继承自A - B的每个项目都可以在所请求的类型为A的地方传递。

通过继承,Liskov替换原则或方法方差称它为多态,名称无关紧要。重要的是以下工作(在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;
}

遗传替代方案

事实上,您可以告诉您要返回IList<TA>并请求一些派生类型(TBTC ...)来填充它。这是正确的,以下示例也适用(在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;
}

或者如果你想保留一个特定的基类型(假设你想要返回IList<A>但它实际上包含从A派生的类的项,那么你可以这样做:

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;
}

你不必,但你可以

好的,如果你真的想说它返回IList<T> where T : A。然后说出来!

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;
}

是的,那个人不能返回T类型的项目和A类型的项目的混合,因为它表示它返回IList<T>而不是{{1}类型的每个项目}也是A类型的项目。


您的代码会发生什么

看看你的代码:

T

当您说要返回public IList<A> MyMethod(){ IList<B> result = new List<B>(); //add some items to result return result; } 时,您正试图返回IList<B>。让我们假设它有效...那么你的方法的调用者会发生什么?我们来看看:

IList<A>

DFTBA!