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;
}
}