Почему цикл for ведет себя иначе при переносе кода VB.NET на C #?

87

Я занимаюсь переносом проекта с 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

Теперь мой вопрос решает вопрос о том, чем Visual Basic отличается от C # с точки зрения Visual Basic с использованием stringValue.Lengthусловия в forцикле, хотя каждый раз, когда цикл возникает, длина строки изменяется. В то время как в C # , если я использую stringValue.Lengthв forусловии цикла он меняет начальное значение каждый раз , когда строка , происходит цикл. Почему это?

Slee423
источник
7
Microsoft четко описывает, в чем ваша проблема ...
Codexer
39
@ Öđěxěŕ: Прекратите, пожалуйста. Если бы роботизированное удаление тегов из заголовка без учета контекста было полезным, веб-сайт это сделал бы.
Ry-
11
@ Çöđěxěŕ Вы можете найти информацию об этом здесь
Пушкин
35
@ Çöđěxěŕ Я не думаю, что эти два сообщения противоречат друг другу. Дело в том, что не вставляйте неловко в заголовок метку вроде «вопрос об этом - тег». Но если заголовок представляет собой законченное предложение, включающее тег, иногда это нормально. «Почему цикл for ведет себя по-другому при переносе» кажется слишком общим заголовком.
Пушкин
26
@ Çöđěxěŕ "перенос кода VB.NET на C #" - это явно полная фраза, которая добавляет полезную информацию к заголовку. Не вносите правки, снижающие ясность. Надеюсь, это понятие достаточно здравое, и нам не нужен мета-пост для его поддержки.
jpmc26 02

Ответы:

115

В C # граничное условие цикла оценивается на каждой итерации. В VB.NET он оценивается только при входе в цикл.

Итак, в версии C # в вопросе, поскольку длина цикла stringValueизменяется в цикле, последнее значение переменной цикла будет изменено.

В VB.NET последнее условие является включающим, поэтому вы должны использовать его <=вместо <C #.

Оценка конечного условия в C # приводит к тому, что даже если оно не меняется, но требует больших затрат на вычисление, его следует вычислять только один раз перед циклом.

Эндрю Мортон
источник
Спасибо за ответ. Теперь я понимаю, что использование <=позволяет мне выполнять итерацию и получать тот же результат, что и код VB. Однако мне больше интересно узнать, почему мне пришлось объявить целочисленную переменную, а VBмне не пришлось. Я собираюсь обновить свой вопрос, чтобы показать тот же результат.
slee423 02
@ slee423 Причина указана в первом предложении моего ответа. Поскольку stringValueв цикле изменяется длина , последнее значение переменной цикла будет изменено.
Эндрю Мортон
1
извинения, спасибо за этот ответ. И спасибо, что проработали для меня более подробно.
slee423 02
1
@ slee423 Я добавил это в ответ, поскольку он действительно проясняет его.
Эндрю Мортон
22

Теперь мой вопрос решается относительно того, чем VB отличается от C # с точки зрения VB, используя условие stringValue.Length в цикле for, хотя каждый раз, когда цикл происходит, длина строки изменяется.

Согласно документации VB.NET :

Если вы измените значение counterwhile внутри цикла, ваш код будет труднее читать и отлаживать. Изменяя значение start,end или stepне влияет на значения итерации , которые были определены , когда цикл был впервые введен.

Таким образом, значение To 10 - stringValue.Lengthоценивается один раз и повторно используется до выхода из цикла.

Однако посмотрите на инструкцию С # for

Если for_conditionотсутствует или оценка дает результат true, управление передается встроенному оператору. Когда и если управление достигает конечной точки встроенного оператора (возможно, в результате выполнения оператора continue), выражения оператора for_iterator, если таковые имеются, оцениваются последовательно, а затем выполняется еще одна итерация, начиная с оценки оператораfor_condition этапе над.

Это в основном означает, что условие ; i <= 10 - stringValueLength; каждый раз оценивается заново.

Итак, как вы видели, если вы хотите реплицировать код, вам нужно объявить последний счетчик в C # перед запуском цикла.

Камило Теревинто
источник
11

Чтобы сделать пример более понятным, я конвертирую оба цикла for в циклы while в C # .

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++;
}

Тогда разница в следующем:

VB.NET кэширует максимальное значение для i, но C # каждый раз пересчитывает его.

Максим Рекуэрда
источник
2
Вам не нужно преобразовывать петли в while
Панайотис Канавос
10
Это помогло мне понять с for loopsсамого начала, я думаю, что они намного понятнее. Вот почему я «перевел» примеры на язык while loops, чтобы помочь понять.
Максим Рекуэрда
7

Поскольку forв VB семантика отличается отfor в C # (или любом другом C-подобном языке)

В VB for оператор специально увеличивает счетчик от одного значения до другого.

В C, C ++, C # и т. Д. forОператор просто вычисляет три выражения:

  • Первое выражение обычно является инициализацией
  • Второе выражение оценивается в начале каждой итерации, чтобы определить, выполнено ли конечное условие.
  • Третье выражение вычисляется в конце каждой итерации, обычно это инкремент.

В VB вы должны предоставить числовую переменную, которая может быть проверена на конечное значение и увеличиваться на каждой итерации.

В C, C ++, C # и т. Д. Эти три выражения минимально ограничены; условное выражение должно оцениваться как истина / ложь (или целое число ноль / ненулевое значение в C, C ++). Вам вообще не нужно выполнять инициализацию, вы можете выполнять итерацию любого типа в любом диапазоне значений, выполнять итерацию указателя или ссылки в сложной структуре или вообще ничего не повторять.

Таким образом, в C # и т. Д. Выражение условия должно полностью оцениваться на каждой итерации, но в VB конечное значение итератора должно оцениваться в начале и не должно оцениваться снова.

C Робинсон
источник