为什么编译器不能理解.Rows返回的是DataRow集合呢?
这是因为DataTable.Rows(DataRowCollection)与枚举的方式有关,部分原因也与foreach的工作方式以及其古老的特性有关。
首先,我们来谈一下foreach:
foreach不是一个实际存在的东西,它更像是一个语言快捷方式,用于在对象上获取一个枚举器并重复调用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发明以来,纯粹基于它们是否具有某些方法/属性,已经可以枚举事物了。
跳到DataTable.Rows;这是一个集合;确切地说是一个DataRowCollection。它通过继承自InternalDataCollectionBase实现了IEnumerable接口,该接口本身仅是一个声明“该类必须具有返回IEnumerator的GetEnumerator()方法”的接口,IEnumerator是一个强制执行“必须具有返回object的Current属性和返回bool的MoveNext()”的接口。
不必实现这些接口即可进行foreach,但这有助于..
..也许您随后会注意到:实现IEnumerator要求Current属性为对象。
这是编译器错误:
public class EnumeratorSchmenumerator : IEnumerator
{
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
时,他们被迫编写一个返回
IEnumerator
的
.GetEnumerator()
方法,这反过来又强制要求
Current
是一个
object
。
那个对象里面有一个
DataRow
,但编译器不知道,所以会将
var
类型定义为
object
。
当你使用
foreach(DataRow r in dt.Rows)
语句时,C#在后台自动添加一个转换为
DataRow
的语句。
在
var
形式中,就像你写了
foreach(object r in dt.Rows)
一样。