8

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

我知道期望 List 参数的方法对 List 不满意,因为它们可能会尝试将 Base 添加到 List 中,这显然是不可能的。

但是,如果我正确理解了这一点,因为 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

4 回答 4

19

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

但是在通用方法中,您必须考虑T. 您也可以使用Cast<T>or OfType<T>with LINQ 来实现类似的功能。

于 2009-03-11T12:17:04.103 回答
2

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

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

那么它应该适合你。

于 2009-03-18T17:22:53.553 回答
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>() );

但是“<Person, Employee>”有点尴尬:/。

于 2009-03-11T12:24:15.413 回答
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<Mammal>.From(kennel.Dogs)

于 2009-09-25T22:23:09.627 回答