C#无法从IEnumerable <base />转换为IEnumerable <derived> </derived>

时间:2009-03-11 12:15:18

标签: c# inheritance collections ienumerable

我在尝试将AddRange(IEnumerable)添加到List时遇到了麻烦。可能是一个经典问题,但我还没有真正得到它。

我知道期望List参数的方法不满足于List,因为他们可能会尝试向List添加Base,这显然是不可能的。

但是,如果我得到正确的,因为IEnumerables本身不能改变,它应该在这种情况下工作。

我想到的代码看起来像这样:

class Foo
{
}

class Bar : Foo
{
}

class FooCol
{
    private List<Foo> m_Foos = new List<Foo> ();

    public void AddRange1(IEnumerable<Foo> foos)
    {
        m_Foos.AddRange (foos); // does work
    }

    public void AddRange2<T>(IEnumerable<T> foos) where T : Foo
    {
        m_Foos.AddRange (foos); // does not work
    }
}

class Program
{
    static void Main(string[] args)
    {
        FooCol fooCol = new FooCol ();

        List<Foo> foos = new List<Foo> ();
        List<Bar> bars = new List<Bar> ();

        fooCol.AddRange1 (foos); // does work
        fooCol.AddRange1 (bars); // does not work

        fooCol.AddRange2 (foos); // does work
        fooCol.AddRange2 (bars); // does work
    }
}

我试图在AddRange2方法中向编译器传递一个提示,但这只是问题所在。

我的思维方式有缺陷吗?这是语言的限制还是设计?

IIRC,Java 1.5中添加了对此类操作的支持,所以也许它将来会在某个时候被添加到C#中......?

4 个答案:

答案 0 :(得分:18)

这是协方差,将在C#4.0 / .NET 4.0中修复。目前,通用选项是最佳答案(适用于IEnumerable<T> - not IList<T> etc)。

但是在通用方法中,你必须考虑T。您还可以使用Cast<T>OfType<T>与LINQ实现类似的目标。

答案 1 :(得分:2)

在C#3.0中,您可以使用“Cast”扩展方法。如果您导入System.Linq然后使用此代码:

public void AddRange2<T>(IEnumerable<T> foos) where T : Foo
{
    m_Foos.AddRange (foos.Cast<Foo>());
}

然后它应该适合你。

答案 2 :(得分:0)

扩展方法有解决方法:

public static IEnumerable<TBase> ToBaseEnumerable<TBase, TDerived>( this IEnumerable<TDerived> items ) where TDerived : TBase {
    foreach( var item in items ) {
        yield return item;
    }
}
...
IEnumerable<Employee> employees = GetEmployees(); //Emplyoee derives from Person
DoSomethingWithPersons( employees.ToBaseEnumerable<Person, Employee>() );

但是“&lt; Person,Employee&gt;”有点尴尬:/。

答案 3 :(得分:0)

当然,强制转换解决方案可能会生成类强制转换异常。 发布可枚举扩展工作的人说这很尴尬。 我想出了一个只有一半尴尬的解决方案,不知道我是否会使用它:

public static class UpTo<T>
{
    public static IEnumerable<T> From<F>(IEnumerable<F> source) where F:T
    {
        // this cast is guaranteed to work
        return source.Select(f => (T) f);
    }
}

用法:

IEnumerable哺乳动物= UpTo&lt; Mammal&gt;。(kennel.Dogs)