C# 为什么可以';我做foreach(DataTable.Rows中的var项)?

C# 为什么可以';我做foreach(DataTable.Rows中的var项)?,c#,.net,datatable,foreach,datarow,C#,.net,Datatable,Foreach,Datarow,我不能做以下事情的原因是什么: foreach (var Item in DataTable.Rows) { 而不是不得不这样做 foreach (DataRow Item in DataTable.Rows) { 我本以为这是可能的,就像在其他数据类型上一样。例如: foreach (var Employee in Staff) { // string[] Staff etc... 当我尝试第一个foreach循环时,出现错误CS0021:无法将带[]的索引应用于“object”类型的表

我不能做以下事情的原因是什么:

foreach (var Item in DataTable.Rows) {
而不是不得不这样做

foreach (DataRow Item in DataTable.Rows) {
我本以为这是可能的,就像在其他数据类型上一样。例如:

foreach (var Employee in Staff) { // string[] Staff etc...
当我尝试第一个foreach循环时,出现错误CS0021:无法将带[]的索引应用于“object”类型的表达式


为什么编译器不能弄明白。行返回一组数据行?

有效地返回
IEnumerable
DataRowCollection
),因此编译器只能选择
对象
作为
var
的类型。如果要使用
var
,请使用
Rows.Cast


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

Brian对此的原因完全正确,但有一种更简单的方法可以避免它:使用:

为什么编译器不能理解。行返回数据行的集合

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

首先介绍一下foreach的

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

类不必实现任何接口或任何特定类型,它只需要有一个名为
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;
  }
}
你可以在午夜到凌晨1点之间,通过计算这数百万次来获取当前时间。枚举不需要集合或移动到任何地方;它只需按要求返回值,并在被告知移动时返回true或false。我概述了这一点,以防你认为“但肯定有些东西必须实现一些IEnumerable..这意味着在那里的某个地方有一些输入..”。否-自从datatable被发明以来,就有可能纯粹根据事物上是否存在某种方法/属性来枚举它们


向前跳转到
DataTable.Rows
;这是一个收藏;准确地说,是一个
数据行集合。它确实实现了
IEnumerable
接口,这是因为它继承了
InternalDataCollectionBase
,后者实现了
IEnumerable
——但它本身只是一个声明“类必须有一个返回
IEnumerator
GetEnumerator()方法”的接口,而
IEnumerator
是一个接口,它强制“必须具有一个返回
对象的
Current
属性和一个返回
bool
MoveNext()

没有必要将这些接口实现为
foreach
功能,但它有帮助

…然后您可能发现了它:实现
IEnumerator
需要
Current
属性是
对象

这是一个编译器错误:

public class EnumeratorSchmenumerator : IEnumerator //look! new bit!
{
  public DateTime Current {
    get{ return DateTime.Now; }
  }
  public bool MoveNext() {
    return DateTime.Now.Hour == 0;
  }
}
“EnumeratorSCHmnumerator”未实现接口成员“IEnumerator.Current”。 [现有]“EnumeratorSCHmnumerator.Current”无法实现“IEnumerator.Current”,因为它没有匹配的返回类型“object”

因此,在选择让
DataRowCollection
实现
IEnumerable
时,他们被迫编写一个
.GetEnumerator()
方法,该方法返回一个
IEnumerator
,从而强制当前的
对象成为
对象

该对象中有一个
DataRow
,但编译器不知道,因此它将
var
键入
对象

当您说
foreach(dt.Rows中的数据行r)
时,代码C#在后台写入以扩展
foreach
将包括对
DataRow
的转换


使用
var
表单,就像您编写的
foreach(dt.Rows中的对象r)

是泛型之前的数据表吗?@Arnis:当然,数据表从1.0开始就存在了。所以-我想这是真正的原因。泰·乔恩,请澄清。:)感谢您提供了这样一个漂亮的解决方案:)我保留了公认的答案作为Brian的答案,但将使用您的答案。是时候多读一点关于枚举的书了……我想说,这个问题的真正解决办法是在这个例子中不使用var…@ck:同意,但是为了与其他LINQ方法相结合,有必要了解
AsEnumerable
public class EnumeratorSchmenumerator : IEnumerator //look! new bit!
{
  public DateTime Current {
    get{ return DateTime.Now; }
  }
  public bool MoveNext() {
    return DateTime.Now.Hour == 0;
  }
}