Для условия VB.NET vs С#

С#:

static class Module1
{     
    public static void Main()
    {    
        for (index = 1; index <= GetCount(); index++) {
            Console.WriteLine("For {0}", index);
        }
        Console.ReadKey();
    }

    public static int GetCount()
    {
        Console.WriteLine("GetCount");
        return 10;
    }
}

Результат (С# перепроверяет условие):

GetCount
For 1
GetCount
For 2
GetCount
For 3
GetCount
For 4
GetCount
For 5
GetCount
For 6
GetCount
For 7
GetCount
For 8
GetCount
For 9
GetCount
For 10
GetCount

VB.NET

Module Module1    
  Sub Main()
    For index = 1 To GetCount()
      Console.WriteLine("For {0}", index)
    Next
    Console.ReadKey()
  End Sub

  Public Function GetCount() As Integer
    Console.WriteLine("GetCount")
    Return 10
  End Function
End Module

Результат (VB.NET не перепроверяет условие):

GetCount
For 1
For 2
For 3
For 4
For 5
For 6
For 7
For 8
For 9
For 10

a) Почему не поддерживает VB.NET "правило" повторного проверки условия For на каждой итерации?
b) Есть ли способ заставить VB повторно проверить это условие?

Ответы

Ответ 1

С# и VB.NET - разные языки, а похожие ключевые слова могут иметь различную семантику.

Из документы для For ... Next (мой акцент):

При запуске цикла For...Next Visual Basic оценивает start, end и step. Это единственный раз, когда он оценивает эти значения.

Из раздела 8.8.3 спецификации С# (то же):

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

Если вы хотите, чтобы каждое условие принудительно проверялось, VB.NET предлагает чрезвычайно гибкую Do... Loop, которая может иметь условие While или Until, работающее в начале или в конце цикла. С этим утверждением условие цикла оценивается каждый раз.

Ответ 2

VB.NET for-loop работает не так, как С#. Он говорит, что переход от этого значения к этому значению. "Это значение" оценивается один раз.

С# one в основном for('initialise stuff';'conditional break stuff';'incremental stuff'), он каждый раз оценивает "материал условного прерывания". С простым for-loop в С# он выглядит так же, как и VB, но он (как вы уже нашли) работает по-другому.

Ответ 3

Вы можете добиться такого же эффекта в VB.NET, используя while(condition).


В этом отличие от компилятора. Компилятор VB.NET ведет себя по-другому.

если вы используете отражатель на VB.NET, вы видите этот код с кодом С#:

[STAThread]
public static void Main()
{
    int VB$t_i4$L0 = GetCount();
    for (int index = 1; index <= VB$t_i4$L0; index++)
    {
        Console.WriteLine("For {0}", index);
    }
    Console.ReadKey();
}

И вот код IL (примечание IL_002):

.method public static void  Main() cil managed
{
  .entrypoint
  .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       47 (0x2f)
  .maxstack  2
  .locals init ([0] int32 index,
           [1] int32 VB$t_i4$L0,
           [2] int32 VB$CG$t_i4$S0)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  call       int32 ConsoleApplication2.Module1::GetCount()
  IL_0007:  stloc.1
  IL_0008:  stloc.0
  IL_0009:  br.s       IL_0021
  IL_000b:  ldstr      "For {0}"
  IL_0010:  ldloc.0
  IL_0011:  box        [mscorlib]System.Int32
  IL_0016:  call       void [mscorlib]System.Console::WriteLine(string,
                                                                object)
  IL_001b:  nop
  IL_001c:  nop
  IL_001d:  ldloc.0
  IL_001e:  ldc.i4.1
  IL_001f:  add.ovf
  IL_0020:  stloc.0
  IL_0021:  ldloc.0
  IL_0022:  ldloc.1
  IL_0023:  stloc.2
  IL_0024:  ldloc.2
  IL_0025:  ble.s      IL_000b
  IL_0027:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
  IL_002c:  pop
  IL_002d:  nop
  IL_002e:  ret
} // end of method Module1::Main

В то время как для кода С# он отличается (проверка находится внутри цикла):

.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       50 (0x32)
  .maxstack  2
  .locals init ([0] int32 index,
           [1] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  stloc.0
  IL_0003:  br.s       IL_001c
  IL_0005:  nop
  IL_0006:  ldstr      "For {0}"
  IL_000b:  ldloc.0
  IL_000c:  box        [mscorlib]System.Int32
  IL_0011:  call       void [mscorlib]System.Console::WriteLine(string,
                                                                object)
  IL_0016:  nop
  IL_0017:  nop
  IL_0018:  ldloc.0
  IL_0019:  ldc.i4.1
  IL_001a:  add
  IL_001b:  stloc.0
  IL_001c:  ldloc.0
  IL_001d:  call       int32 Module1::GetCount()
  IL_0022:  cgt
  IL_0024:  ldc.i4.0
  IL_0025:  ceq
  IL_0027:  stloc.1
  IL_0028:  ldloc.1
  IL_0029:  brtrue.s   IL_0005
  IL_002b:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
  IL_0030:  pop
  IL_0031:  ret
} // end of method Module1::Main

Ответ 4

Причина в том, что VB For может быть переведен на нечто вроде этого в С#:

int count = GetCount();
for (index = 1; index <= count; index++)
{
}

или, используя linq, чтобы походить на VB:

foreach(int i in Enumerable.Range(1,GetCount())
{
}

В обоих случаях (и в версии VB) GetCount() вызывается один раз, поэтому один вызов только Console.WriteLine("GetCount").

Ответ 5

Ну, короткий ответ - это разные языки, и у них немного другое отношение к этому ключевому слову.

В С# итерация продолжается до тех пор, пока условие завершения не станет равным false, и оно будет оцениваться на каждой итерации.

В VB.NET мы повторяем одно время для каждого целочисленного значения от начала до конца (при условии, что ключевое слово шага отсутствует), а затем остановимся. Конец оценивается один раз в начале и что он.

Вы можете приблизиться к типу поведения С# в VB.NET с помощью цикла Do.

Ответ 6

VB.NET For..To цикл нужно только вызвать метод один раз. Он сохраняет это значение и проверяет его на каждой итерации цикла. Где цикл С# for вызывает его на каждой итерации. Вы можете придумать версию С# для фантастического синтаксиса для:

index = 1;
while(index <= GetCount()) {
  Console.WriteLine("For {0}", index);
  index++;
}

И если вы попытаетесь записать это как в С#, так и в VB.NET, они будут работать одинаково.

Ответ 7

Как говорили другие, VB.Net и С# - разные языки, поэтому такие различия могут произойти.

Если вы хотите принудительно переопределить условие цикла в VB.Net, вы должны переписать цикл For как цикл While