在foreach循环中投射

时间:2014-04-02 08:35:17

标签: c#

我遇到了以下代码:

foreach (var row in datatable.Rows.Cast<DataRow>())
{
  some code here
}

现在,我认为在foreach循环中进行转换是不正确的,因为我认为每次循环都会进行转换。但是,我可能错了,也许编译器足够聪明?

在相关的说明中:我怎么能为自己想出这样的事情呢?

3 个答案:

答案 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
    }
}