【问题标题】:C# casting question: from IEnumerable to custom typeC# 转换问题:从 IEnumerable 到自定义类型
【发布时间】:2011-02-23 16:49:51
【问题描述】:

我有一个名为Rows 的自定义类,它实现了IEnumerable<Row>。我经常在 Rows 实例上使用 LINQ 查询:

Rows rows = new Rows { row1, row2, row3 };
IEnumerable<Row> particularRows = rows.Where<Row>(row => condition);

我希望能够做到以下几点:

Rows rows = new Rows { row1, row2, row3 };
Rows particularRows = (Rows)rows.Where<Row>(row => condition);

但是,我收到“System.InvalidCastException:无法将 'WhereEnumerableIterator1[NS.Row]' 类型的对象转换为 'NS.Rows' 类型”。我确实有一个Rows 构造函数采用IEnumerable&lt;Row&gt;,所以我可以这样做:

Rows rows = new Rows { row1, row2, row3 };
Rows particularRows = new Rows(rows.Where<Row>(row => condition));

然而,这看起来很庞大,我希望能够将IEnumerable&lt;Row&gt; 转换为Rows,因为Rows 实现了IEnumerable&lt;Row&gt;。有什么想法吗?

【问题讨论】:

  • 只是一个备注(与您的问题无关):您不需要为Where指定泛型类型参数,它是由编译器自动推断的
  • @Thomas:我实际上有几个不同的继承类,它们实现了不同的泛型IEnumerable&lt;T&gt;,所以泛型类型参数需要在我的真实代码中。我这里显示的示例不需要它,你是对的。

标签: c# linq inheritance interface casting


【解决方案1】:

救援的扩展方法。

public static Rows ToRows(this IEnumerable<Row> source)
{
  return new Rows(source);
}

Rows specificRows = rows.Where(...).ToRows();


“铸造”不适合解决这个问题。每个Rows 都是IEnumerable&lt;Row&gt;,但不是每个IEnumerable&lt;Row&gt; 都是Rows

【讨论】:

  • 这可能比我想的要好,即覆盖Rows 中的Where 等以返回Rows 而不是IEnumerable&lt;Row&gt;。跨度>
【解决方案2】:

如果对象实际上是该类的实例,则只能将对象强制转换为特定类。

在您的情况下,您从 LINQ 调用中获得的 IEnumerable&lt;Row&gt; 不是 Rows 实例,并且 .Net 运行时无法神奇地将其转换为 Rows 实例。

相反,您需要创建一个从IEnumerable&lt;Row&gt; 创建新的Rows 实例的函数。该函数可能应该是构造函数重载或扩展方法。

顺便提一下,标准的 .Net 命名约定表明您的 Rows 类实际上应该称为 RowCollection

【讨论】:

  • 是否有网站描述了 Collection v. List v. Enumerable 的不同含义?
  • IListICollectionIEnumerable是三个不同的接口;您可以在 MSDN 上看到差异。自定义集合类应始终以 Collection 结尾。
  • 如果我的课程没有实现ICollection,真的应该叫RowCollection吗?
  • 是的,应该。它也应该(但不一定)实现IList&lt;Row&gt;
【解决方案3】:

虽然Rows 可以实现IEnumerable&lt;Row&gt;,但这并不意味着任何IEnumerable&lt;Row&gt; 都是Rows,因此编译器不能让您做出这样的假设。

【讨论】:

    【解决方案4】:

    您必须记住,虽然您可以向上转换继承链(Row -> IEnumerable),但没有可靠的方法可以向下转换继承链。

    考虑以下几点:

    List<Row> rows = new List<Row>();
    
    // Fill the list
    IEnumerable<Row> rowsAsEnumerable = (IEnumerable<Row>)rows;
    
    Rows realRows = (Rows)rowsAsEnumerable;
    

    由于 rowsAsEnumerable 从来都不是 Rows 对象,系统将如何处理转换?

    答案是不能,因为即使 Rows 对象始终是 IEnumerable,IEnumerable 也不总是 Rows 对象。

    更新

    由于我不喜欢成为梦想粉碎者,所以我将重申其他人所说的话。

    您可以使用简单的扩展方法使您的示例中的内容更简洁:

    public static Rows ToRows(this IEnumerable<Row> rowsAsEnum)
    {
        return new Rows(rowsAsEnum);
    }
    

    现在您可以将呼叫更改为:

    Rows particularRows = rows.Where<Row>(row => condition).ToRows();
    

    【讨论】:

    • 我以为我撞到了这堵墙,但我有希望!你在这里左右粉碎了我的铸造希望...... :(
    • @Sarah:别担心;有可能的。你需要写一个ToRows()扩展方法。
    • @Sarah - 我讨厌听到我正在破灭希望。虽然您不能直接转换,但您可以在此处使用其他解决方法之一(包括在 Rows 类中定义显式和隐式转换)。
    【解决方案5】:

    基本上,您在此处提出的任何解决方案都将涉及环绕采用 IEnumerable 的构造函数

    我的原始答案定义了强制转换解决方案(但我未能编译并看到错误)。

    我要做的是为 IEnumerable 定义一个扩展方法:

    public static Rows ToRows(this IEnumerable<Row> rows)
    {
      return new Rows(rows);
    }
    

    那么你就可以相当干净地使用它了:

    public void Foo(IEnumerable<Row> rows)
    {
       Rows r = rows.Where(r => condition).ToRows();
    }
    

    【讨论】:

      【解决方案6】:

      这行得通吗?

      Rows particularRows = (Rows)(rows.Where<Row>(row => condition).ConvertAll(r=>(Row)r));
      

      您还可以为上述内容添加一个扩展方法包装器(这将是最干净的解决方案),以便您可以执行以下操作:

      Rows particularRows = rows.Where<Row>(row => condition).ToRows();
      

      【讨论】:

      • "无法将类型 'System.Collections.Generic.List' 转换为 'NS.Rows'"
      【解决方案7】:

      你需要在你的类中定义一个explicit cast

      public class Rows : IEnumerable<Row>
      {
          public static explicit operator MyType(IEnumerable<Row> x)
          {
              return new Rows(x); // using your constructor
          }
      }
      

      这将告诉 C# 允许你的演员:

      Rows particularRows = (Rows)rows.Where<Row>(row => condition);
      

      【讨论】:

      • 这看起来很有希望,但我得到了“'NS.Rows.explicit operator NS.Rows(System.Collections.Generic.IEnumerable)': user-defined conversions to or from an接口不允许”。
      • 我修改了转换...但是你们错了。这会奏效。
      • @David - 你需要阅读明确的内容以及它是如何工作的。明确的做法是说“可以从 IEnumerable 转换为 Rows,这是实现”。这将消除错误消息。
      猜你喜欢
      • 1970-01-01
      • 2013-05-15
      • 2012-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多