为什么 for 循环在将 VB.NET 代码迁移到 C # 时有不同的行为?

我正在将一个项目从 Visual Basic 迁移到 C # ,我必须改变使用 for循环的声明方式。

在 VB.NET 中,for循环声明如下:

Dim stringValue As String = "42"


For i As Integer = 1 To 10 - stringValue.Length
stringValue = stringValue & " " & CStr(i)
Console.WriteLine(stringValue)
Next

产出:

42 1
42 1 2
42 1 2 3
42 1 2 3 4
42 1 2 3 4 5
42 1 2 3 4 5 6
42 1 2 3 4 5 6 7
42 1 2 3 4 5 6 7 8

在 C # 中,for循环声明如下:

string stringValue = "42";


for (int i = 1; i <= 10 - stringValue.Length; i ++)
{
stringValue = stringValue + " " + i.ToString();
Console.WriteLine(stringValue);
}

结果是:

42 1
42 1 2
42 1 2 3

这显然是不正确的,所以我不得不稍微改变代码,包括一个整数变量,将持有字符串的长度。

请参阅以下代码:

string stringValue = "42";
int stringValueLength = stringValue.Length;


for (int i = 1; i <= 10 - stringValueLength; i ++)
{
stringValue = stringValue + " " + i.ToString();
Console.WriteLine(stringValue);
}

结果是:

42 1
42 1 2
42 1 2 3
42 1 2 3 4
42 1 2 3 4 5
42 1 2 3 4 5 6
42 1 2 3 4 5 6 7
42 1 2 3 4 5 6 7 8

现在我的问题解决了 VisualBasic 与 C # 在使用 for循环中的 stringValue.Length条件方面的区别,尽管每次循环发生时字符串的长度都会发生变化。而在 C # 中,如果我在 for循环条件中使用 stringValue.Length,那么每次循环发生时它都会更改初始字符串值。为什么会这样?

6353 次浏览

In C#, the loop boundary condition is evaluated on each iteration. In VB.NET, it is only evaluated on entry to the loop.

So, in the C# version in the question, because the length of stringValue is being changed in the loop, the final loop variable value will be changed.

In VB.NET, the final condition is inclusive, so you would use <= instead of < in C#.

The end condition evaluation in C# has the corollary that even if it doesn't vary but it is expensive to calculate, then it should be calculated just once before the loop.

Now my question resolves around how VB differs to C# in terms of VB using the stringValue.Length condition in the for loop even though each time the loop occurs the length of the string changes.

According to the VB.NET documentation:

If you change the value of counter while inside a loop, your code might be more difficult to read and debug. Changing the value of start, end, or step doesn't affect the iteration values that were determined when the loop was first entered.

So, the value of To 10 - stringValue.Length is evaluated once and reused until the loops exit.

However, look at c#'s for statement

If the for_condition is not present or if the evaluation yields true, control is transferred to the embedded statement. When and if control reaches the end point of the embedded statement (possibly from execution of a continue statement), the expressions of the for_iterator, if any, are evaluated in sequence, and then another iteration is performed, starting with evaluation of the for_condition in the step above.

Which basically means that the condition ; i <= 10 - stringValueLength; is evaluated again each time.

So, as you saw, if you want to replicate the code, you need to declare the final counter in c# before starting the loop.

In order to make the example more understandable, I will convert both for loops into C# while loops.

VB.NET

string stringValue = "42";


int min = 1;
int max = 10 - stringValue.Length;
int i = min;
while (i <= max)
{
stringValue = stringValue + " " + stringValue.Length.ToString();
Console.WriteLine(stringValue);
i++;
}

C#

string stringValue = "42";


int i = 1;
while (i <= 10 - stringValue.Length)
{
stringValue = stringValue + " " + stringValue.Length.ToString();
Console.WriteLine(stringValue);
i++;
}

The difference is then:

VB.NET caches the maximum value for i, but C# recomputes it every time.

Because the for in VB is a different semantic than the for in C# (or any other C-like language)

In VB, the for statement is specifically incrementing a counter from one value to another.

In C, C++, C#, etc., the for statement simply evaluates three expressions:

  • The first expression is customarily an initialization
  • The second expression is evaluated at the start of each iteration to determine whether the terminal condition has been met
  • The third expression is evaluated at the end of each iteration, which is customarily an incrementer.

In VB, you must supply a numeric variable which can be tested against a terminal value and incremented on each iteration

In C, C++, C#, etc., the three expressions are minimally constrained; the conditional expression must evaluate to a true/false (or integer zero/non-zero in C, C++). You don't need to perform an initialization at all, you can iterate any type over any range of values, iterate a pointer or reference over a complex structure, or not iterate anything at all.

So, in C#, etc., the condition expression must be fully evaluated on each iteration, but in VB, the terminal value of the iterator must be evaluated at the beginning, and need not be evaluated again.