我遇到了以下代码:
foreach (var row in datatable.Rows.Cast<DataRow>())
{
some code here
}
现在,我认为在foreach循环中进行转换是不正确的,因为我认为每次循环都会进行转换。但是,我可能错了,也许编译器足够聪明?
在相关的说明中:我怎么能为自己想出这样的事情呢?
答案 0 :(得分:4)
好吧调用 Cast<DataRow>
一次,但这实际上会在获取每个项目时进行投射。所以是每次迭代的一个强制转换(至少可能; Cast
在知道它没有必要时有优化)但是只有一个方法调用。 Cast
返回的迭代器将从源(这里datatable.Rows
)获取项目(懒惰)并按照循环的请求投射每个项目。
有关详细信息,请参阅我的Edulinq post on Cast
。
虽然由于Cast
懒惰,这里的水域有点混乱,但重要的是要记住in
右侧的表达式只会被评估一次。所以形式循环:
foreach (var item in complicatedExpression)
{
...
}
相当于:
var items = complicatedExpression;
foreach (var item in items)
{
...
}
编译器将生成一次调用GetEnumerator()
的代码,然后使用其结果迭代集合。
对于这个具体的例子,使用Cast
的替代方法是让编译器将转换放在循环中:
foreach (DataRow row in datatable.Rows)
{
some code here
}
编译器将在从迭代器中提取它时隐式地对每个项执行强制转换。这有点偷偷摸摸,因为它不是显然是铸造。
至于你怎么知道发生了什么 - 你总是可以用ildasm
查看IL。这可能非常有启发性,但有些耗时。
答案 1 :(得分:1)
IEnumerable<T>.Cast<TResult>
扩展方法返回一个懒惰评估的枚举。这意味着每次您请求下一个&#39;从它的项目,它从基础数据源(在您的情况下为数据表)中检索它,并将其转换为指定的类型。因此,使用foreach
进行投射并不正确,因为投射过程并不太多,并且在必要时会对每个项目进行投射。添加.ToList()
并不是更好,因为即使您不需要,例如,如果您使用break
退出循环,也可以强制转换每个项目。< / p>
您可以通过阅读the documentation并阅读有关该方法的文章来自行查找。
答案 2 :(得分:0)
当你有:
foreach(SomeType name in {expression})
{
// your code
}
{expression}
仅评估一次,因此在您的示例中,Cast<DataRow>()
仅被调用一次。然后处理{expression}
的结果进行枚举,变得类似(取决于许多事情,例如{expression}
支持的结果的API):
using(var iter = {expression}.GetEnumerator())
{
while(iter.MoveNext()
{
SomeType name = iter.Current; // location on recent compilers
// your code
}
}