31

我不能执行以下操作是否有原因:

foreach (var Item in DataTable.Rows) {

而不是必须做

foreach (DataRow Item in DataTable.Rows) {

我原以为这是可能的,就像在其他数据类型上一样。例如:

foreach (var Employee in Staff) { // string[] Staff etc...

当我尝试第一个 foreach 循环时,我收到错误CS0021: Cannot apply indexing with [] to an expression of type 'object'

为什么编译器无法确定 .Rows 返回 DataRows 的集合?

4

3 回答 3

30

Rows有效地返回IEnumerable( DataRowCollection),因此编译器只能object选择var. Rows.Cast<DataRow>如果您想使用,请使用var.

Cast在Enumerable上定义,因此您必须包含 System.Linq。

于 2010-02-24T12:09:20.443 回答
24

Brian 关于这个原因是绝对正确的,但是有一种更简单的方法可以避免它:使用DataTableExtensions.AsEnumerable()

foreach (var row in DataTable.AsEnumerable())
{
    ...
}
于 2010-02-24T12:10:07.703 回答
2

为什么编译器无法确定 .Rows 返回 DataRows 的集合?

因为 DataTable Rows (DataRowCollection) 与枚举一起工作的方式,也部分与foreach工作方式有关,以及这一切有多古老。

首先介绍一下foreach

foreach不是真的东西;它更像是 while 循环的语言快捷方式,它获取对象上的枚举器并重复调用MoveNext, MoveNext, MoveNext... 直到MoveNext返回 false,此时它停止循环。while 循环体所做的第一件事是调用Current枚举器,并将响应加载到您在foreach声明中指定的变量中。您编写的循环体被放入 while 循环体中,在第一个位之后。对你来说,它看起来像是foreach一个真正的循环。实际上,这就像编译器的查找/替换练习。C# 中的很多东西都是这种方式——新特性和语法减少只是以旧方式执行代码的一种查找/替换方式。

为了使foreach一个类不必实现任何接口或成为任何特定类型,它只需要有一个称为GetEnumerator()返回“可以枚举的东西”的方法。

“可以枚举的东西”被定义为“具有返回任何类型对象的bool MoveNext()方法和属性的类”。Current同样,这个“可以枚举的东西”不必实现任何接口,或者是任何特定类型;它只需要一种方法和一种具有特定名称和特定签名的属性。整个事情就像魔术一样,在任何地方都没有限制:

public class ForeachableThing{
  public EnumeratorSchmenumerator GetEnumerator(){
    return new EnumeratorSchmenumerator();
  }
}

public class EnumeratorSchmenumerator{
  public DateTime Current {
    get{ return DateTime.Now; }
  }
  public bool MoveNext() {
    return DateTime.Now.Hour == 0;
  }
}

您可以 foreach 数百万次以获取当前时间,介于午夜和凌晨 1 点之间。枚举不需要有集合或移动到任何地方;它只需要按要求返回值,并在被告知移动时返回 true 或 false。我概述了这一点,以防你认为“但肯定会有一些东西必须实现一些 IEnumerable.. 这意味着在某处有一些输入和..”。不 - 自从数据表被发明以来,就可以纯粹根据它们是否有某种方法/属性来枚举事物


跳到DataTable.Rows; 这是一个集合;准确地说DataRowCollection。它确实实现了IEnumerable接口,因为它继承自InternalDataCollectionBase,它实现IEnumerable了 - 但它本身只是一个接口,它声明“类必须有一个GetEnumerator()返回一个IEnumerator”的方法,并且IEnumerator是一个强制“必须有一个返回的Current属性objectMoveNext()返回bool".

没有必要实现这些接口foreach,但它有帮助..

..也许你然后发现了它:实现IEnumerator要求Current属性是object.

这是一个编译器错误:

public class EnumeratorSchmenumerator : IEnumerator //look! new bit!
{
  public DateTime Current {
    get{ return DateTime.Now; }
  }
  public bool MoveNext() {
    return DateTime.Now.Hour == 0;
  }
}

“EnumeratorSchmenumerator”未实现接口成员“IEnumerator.Current”。[现有] 'EnumeratorSchmenumerator.Current' 无法实现 'IEnumerator.Current' 因为它没有匹配的返回类型 'object'

因此,在选择使用该DataRowCollection工具IEnumerable时,他们被迫编写一个.GetEnumerator()返回 an 的方法IEnumerator,而这反过来又迫使 theCurrent成为一个object

该对象内部有一个DataRow,但编译器不知道,所以它键入varas object

当您说foreach(DataRow r in dt.Rows)C# 在幕后编写的代码以扩展时,foreach将包括转换为DataRow.

有了var表格,就像你写的一样foreach(object r in dt.Rows)

于 2020-07-08T11:02:17.160 回答