迭代变量何时在for循环中递增 [英] When does iteration variable in for loop increment

查看:276
本文介绍了迭代变量何时在for循环中递增的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在阅读Albahari的O'Reily书,<<坚果壳中的C#,并且在《 Linq查询》一章中.他正在描述执行Linq查询时延迟执行和捕获变量的影响.他列举了以下常见错误示例:

I'm currently reading Albahari's O'Reily book, C# in a Nutshell and am in the Linq Query chapter. He is describing the effect of delayed execution and variable capturing when making Linq querys. He gives the following example of a common mistake:

IEnumerable<char> query = "Not what you might expect";
string vowels = "aeiou";
for (int i = 0; i < vowels.Length; i++)
{
    query = query.Where(c => c != vowels[i]);
}
foreach (var c in query)
{
    Console.WriteLine(c);
}
Console.Read();

枚举查询后将抛出IndexOutOfRangeException,但这对我来说没有任何意义.我希望,由于延迟执行和变量捕获的影响,Where运算符c => c!= vowles[i]中的lambda表达式将对整个序列简单地在c => c != vowels[4]处求值.我继续进行调试,以查看抛出异常时发现i的值是多少,并发现其值为5?因此,我继续将for循环中的condition子句更改为i < vowels.Length-1;,实际上没有引发任何异常.是for循环在最后一次迭代中将i迭代为5,还是linq在做其他事情?

An IndexOutOfRangeException is thrown once the query is enumerated but this doesn't make any sense to me. I would expect that the lambda expression in the Where operator c => c!= vowles[i] would simply evaluate at c => c != vowels[4] for the entire sequence, due to the effect of delayed execution and variable capturing. I went ahead and debugged to see what value i had when the exception is thrown and found out it had the value of 5? So i went ahead and changed the condition clause, in the for loop, to i < vowels.Length-1; and indeed no exception was thrown. Is the for loop iterating the i at the very last iteration to 5 or is linq doing somenthing else?

推荐答案

出于所有意图和目的(除了捕获的变量之外),这是

For all intents and purposes (other than captured variables that is), this:

for (int i = 0; i < 10; i++)
    ....

可以改写为:

int i = 0;
while (i < 10)
{
    ....
    i++;
}

因此,如您所见,仅当条件为false时,迭代才会停止,并且条件为false时,我必须等于或大于10.

So as you see, the iteration stops only when the condition is false, and for the condition to be false, i has to be equal to or greater than 10.

实际上,如果我在 LINQPad 中尝试该程序:

In fact, if I try this program in LINQPad:

void Main() { }

public static void Test1()
{
    for (int i = 0; i < 10; i++)
        Console.WriteLine(i);
}

public static void Test2()
{
    int i = 0;
    while (i < 10)
    {
        Console.WriteLine(i);
        i++;
    }
}

然后检查生成的IL,让我将两种方法放在一起:

And then check the generated IL, let me put the two methods side by side:

Test1:                                            Test2:
IL_0000:  ldc.i4.0                                IL_0000:  ldc.i4.0    
IL_0001:  stloc.0     // i                        IL_0001:  stloc.0     // i
IL_0002:  br.s        IL_000E                     IL_0002:  br.s        IL_000E
IL_0004:  ldloc.0     // i                        IL_0004:  ldloc.0     // i
IL_0005:  call        System.Console.WriteLine    IL_0005:  call        System.Console.WriteLine
IL_000A:  ldloc.0     // i                        IL_000A:  ldloc.0     // i
IL_000B:  ldc.i4.1                                IL_000B:  ldc.i4.1    
IL_000C:  add                                     IL_000C:  add         
IL_000D:  stloc.0     // i                        IL_000D:  stloc.0     // i
IL_000E:  ldloc.0     // i                        IL_000E:  ldloc.0     // i
IL_000F:  ldc.i4.s    0A                          IL_000F:  ldc.i4.s    0A 
IL_0011:  blt.s       IL_0004                     IL_0011:  blt.s       IL_0004
IL_0013:  ret                                     IL_0013:  ret         

然后您会看到它生成了完全相同的代码.

Then you can see that it generated the exact same code.

现在,编译器将确保您无法在尝试访问该变量的for循环后的 之后编写代码,但是如果您捕获了变量(如代码所示),则将访问该变量就像循环结束时一样,只有条件为false时,循环才会自行结束.

Now, the compiler will ensure you cannot write code after the for-loop that tries to access the variable, but if you capture the variable, as your code shows, then you will access the variable as it was when the loop ended, and the loop will only end by itself when the condition is false.

这样,您假设i等于字符串中最后一个字符的索引是false,它等于刚过去的索引,因此当您尝试执行时,您将获得索引超出范围的异常代表.

As such, your assumption that i would equal the index of the last character in the string is false, it will equal the index just past it and thus you'll get index out of range exceptions when you try to execute the delegate.

这是一个简单的 .NET小提琴,它演示了以下程序:

Here's a simple .NET Fiddle that demonstrates, this program:

using System;

public class Program
{
    public static void Main()
    {
        Action a = null;
        for (int index = 0; index < 10; index++)
            a = () => Console.WriteLine(index);

        a();
    }
}

输出10.

这篇关于迭代变量何时在for循环中递增的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆