将通用的对象集合传递给需要基类型集合的方法

时间:2009-07-23 20:37:01

标签: c# collections casting types

假设我有一个期望基类型的泛型集合参数的方法,请参阅下面的 Test.MethodA(IEnumerable(BaseClass)listA)。为什么我传递代码不会构建的派生类型的集合? DerivedClass 的所有实例都不是 BaseClass 吗?

我本来可以创建一个新的 List(BaseClass)并将其传递给 MethodA(IEnumerable(BaseClass)listA)。但我认为C#足够聪明,知道DerivedClass的集合具有与BaseClass集合相同的所有属性。

使用List.Cast(T)()方法,因为我已经展示了解决此问题的最佳方法吗?

abstract class BaseClass
{
    public int SomeField;
    public abstract string SomeAbstractField
    {
        get;
    }
}

class DerivedClass:BaseClass
{
    public override string SomeAbstractField
    {
        get { return "foo"; }
    }
}

class TestClass
{ 

    public void MethodA(IEnumerable<BaseClass> listA)
    {

    }

    public void MethodB()
    {
        List<DerivedClass> listB = new List<DerivedClass>();

        //Error 16  The best overloaded method match for 
        //TestClass.MethodA(List<BaseClass>)' 
        //has some invalid arguments
        this.MethodA(listB);

        //this works
        this.MethodA(listB.Cast<BaseClass>());
    }
}

2 个答案:

答案 0 :(得分:10)

Cast<>()是目前解决问题的最佳方法。您的原始版本在C#4.0 / .NET 4.0中可以正常工作,其中IEnumerable<T>T中是协变的。

(我刚刚验证它在.NET 4.0 beta 1下编译。)

在.NET 4.0和C#4出现之前,泛型是不变的 - IEnumerable<object>IEnumerable<string>实际上是无关的接口。即使在.NET 4.0中,您也无法使用List<T>作为参数类型执行此操作 - 仅接口和委托将是变体,即使这样,仅当type参数仅 在适当的位置使用(协方差的输出位置,逆变的输入位置)。

要了解有关C#4差异的更多信息,请阅读Eric Lippert关于它的优秀series of blog posts

答案 1 :(得分:1)

在一般情况下(即对于可修改的集合),并非“DerivedClass的集合具有与BaseClass集合相同的所有属性”的情况:进入Fruit的集合,您可以插入Banana,Apple和梨 - 在橙色的集合中,你不能,所以,你看,它没有具有相同的属性(正在修改)。

IEnumerable是一个不同的情况,因为它不可修改,就像@Jon所说的那样,对于4.0中的特殊情况已经删除了这个过度的限制。