【问题标题】:C# Cannot convert from IEnumerable<Base> to IEnumerable<Derived>C# 无法从 IEnumerable<Base> 转换为 IEnumerable<Derived>
【发布时间】:2010-10-12 15:59:35
【问题描述】:

我最近在尝试将 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# 中...?

【问题讨论】:

  • 其他各种问题的重复,包括最近的stackoverflow.com/questions/632399
  • 这似乎(实际上)是 this recent SO thread 的副本。
  • 确实,对不起。我的 google-jutsu 仍然需要改进。
  • 没问题。最好有一个每个标签的常见问题解答,IMO...
  • @Jon - 不错的主意;一个用于 UserVoice?拥有一个每个标签的登陆页面(wiki 可编辑)的能力,我们可以在其中放置这种类型的东西......

标签: c# inheritance collections ienumerable


【解决方案1】:

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

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.From(kennel.Dogs)

【讨论】:

    【解决方案2】:

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

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

    那么它应该适合你。

    【讨论】:

      【解决方案3】:

      有扩展方法的解决方法:

      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>() );
      

      但是“”有点尴尬:/。

      【讨论】:

        【解决方案4】:

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

        但在泛型方法中,您必须根据T 进行思考。您还可以将Cast&lt;T&gt;OfType&lt;T&gt; 与LINQ 一起使用来实现类似的效果。

        【讨论】:

        • 感谢新术语,协方差。不知道存在。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-12
        相关资源
        最近更新 更多