VB.NET和C#在条件语句中有何不同?

16

C#:

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

结果(C#会重新检查条件):

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重新检查这个条件?


尽管它们看起来相同,但它们实际上不是同一类型的循环。我无法解释得足够好,但期待能得到一个可以解释清楚的答案 :) - Tom Gullen
7个回答

11

C#和VB.NET是不同的编程语言,相似的关键字可能具有不同的语义。

来自For ... Next文档(我强调):

当一个For...Next循环开始时,Visual Basic会评估startendstep。这是它评估这些值的唯一时间。

来自C#规范8.8.3节(同样):

当控制到达嵌套语句的终点(可能是通过执行continue语句),for-iterator的表达式(如果有)会按顺序进行评估,然后执行另一个迭代,从上述“以评估for-condition开始”的步骤开始。

如果您确实想要每次都强制检查条件,则VB.NET提供了非常灵活的Do ... Loop,它可以具有While或Until条件,在循环的开始或结束时运行。使用该语句,循环条件会在每次循环中被评估。


不确定 VB 关键字,但添加一个 If 'condition' then **'exit for'**。 - George Duckett
1
相关链接:https://msdn.microsoft.com/zh-cn/library/5ebk1751.aspx 当 For Each…Next 语句运行时,Visual Basic 仅在循环开始之前评估集合一次。如果语句块更改元素或组,则这些更改不会影响循环的迭代。 在该语句中,“element”和“group”指的是:For Each element [ As datatype ] In group - Panzercrisis

3

您可以使用 while(condition) 在VB.NET中实现相同的效果。


这就是与编译器的区别。 VB.NET编译器只是表现得不同。

如果您在VB.NET上使用反射器,您会看到这个C#反射代码:

[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

对于与C#相关的代码,情况有所不同(检查在循环内部):

.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

2
不完全是编译器,更多的是语言。 - AakashM

2
VB.NET的for循环与C#的不同。它指定从一个值到另一个值。'那个值'只会被计算一次。
C#的for循环基本上是for('初始化';'条件判断';'增量'),每次都会计算'条件判断'。 在C#中,一个简单的for循环看起来与VB相同,但是它们(如你所发现的)运行方式不同。

2

原因是VB的For循环可以在C#中转换为类似以下的代码:

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

或者,使用 LINQ 来类似于 VB:
foreach(int i in Enumerable.Range(1,GetCount())
{
}

在这两种情况下(以及VB版本中),GetCount() 仅被调用一次,因此只会有一次对 Console.WriteLine("GetCount") 的调用。

1

简而言之,C#和VB.NET是不同的语言,并且对此关键字的解释略有不同。

在C#中,迭代会继续执行直到终止条件为false,并且每次迭代都会进行评估。

在VB.NET中,我们会根据Start到End之间的整数值迭代一次(前提是step关键字不存在),然后停止。End在开始时只评估一次。

您可以使用Do循环在VB.NET中实现接近于C#的行为。


0

正如其他人所说,VB.Net和C#是不同的语言,因此这种差异是可以发生的。

如果你想在VB.Net中强制重新评估循环条件,你必须将For循环重写为While循环。


0

VB.NET的For..To循环只需要调用一次方法。它会存储该值并在每个循环迭代中检查它。而C#的for循环在每次迭代时都会调用它。你可以将C#版本视为以下语法的高级形式:

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

如果你尝试在C#和VB.NET中编写这个代码,它们将会执行相同的操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接