GOTO与DO WHILE差异

时间:2015-07-28 07:22:39

标签: c# loops do-while goto

以下2个C#片段之间的执行存在差异吗?

do
{
    Console.WriteLine(x.ToString());
    ++x;
} 
while (x < 7);

label:
{
    Console.WriteLine(x.ToString());
    ++x;
}
if (x < 7) goto label;

我想弄清楚为什么这么糟糕。 感谢。

编辑:如果我添加括号,则代码段非常相似。

EDIT2:在Visual Studio中,我点击了Go to Disassembly,我得到以下代码:

            do
            {
00000037  nop 
                Console.WriteLine(x.ToString());
00000038  lea         ecx,[ebp-40h] 
0000003b  call        63129C98 
00000040  mov         dword ptr [ebp-48h],eax 
00000043  mov         ecx,dword ptr [ebp-48h] 
00000046  call        63148168 
0000004b  nop 
                ++x;
0000004c  inc         dword ptr [ebp-40h] 
            } 
0000004f  nop 
            while (x < 7);
00000050  cmp         dword ptr [ebp-40h],7 
00000054  setl        al 
00000057  movzx       eax,al 
0000005a  mov         dword ptr [ebp-44h],eax 
0000005d  cmp         dword ptr [ebp-44h],0 
00000061  jne         00000037 

            label:
            {
                Console.WriteLine(x.ToString());
00000069  lea         ecx,[ebp-40h] 
0000006c  call        63129C98 
00000071  mov         dword ptr [ebp-4Ch],eax 
00000074  mov         ecx,dword ptr [ebp-4Ch] 
00000077  call        63148168 
0000007c  nop 
                ++x;
0000007d  inc         dword ptr [ebp-40h] 
            }
00000080  nop 
            if (x < 7) goto label;
00000081  cmp         dword ptr [ebp-40h],7 
00000085  setge       al 
00000088  movzx       eax,al 
0000008b  mov         dword ptr [ebp-44h],eax 
0000008e  cmp         dword ptr [ebp-44h],0 
00000092  jne         00000097 
00000094  nop 
00000095  jmp         00000068

区别在于无条件跳跃。

2 个答案:

答案 0 :(得分:9)

不,我甚至认为while的实现方式与后面的相同。

使用goto时的不好之处在于它鼓励在您的代码中来回传递(也称为“意大利面条代码&#39;这是一团糟”)。它使您的代码极难阅读,调试和分析,并且它引入了错误,因为您无法真正了解正在发生的事情。

while的好处是,可以理解它,编译器可以理解它,所以它可以给你很好的警告。

答案 1 :(得分:2)

让我们来看看IL:

.method private hidebysig 
    instance void While () cil managed 
{
    // Method begins at RVA 0x2050
    // Code size 31 (0x1f)
    .maxstack 2
    .locals init (
        [0] int32 x,
        [1] bool CS$4$0000
    )

    IL_0000: nop
    IL_0001: ldc.i4.0
    IL_0002: stloc.0
    // loop start (head: IL_0003)
        IL_0003: nop
        IL_0004: ldloca.s x
        IL_0006: call instance string [mscorlib]System.Int32::ToString()
        IL_000b: call void [mscorlib]System.Console::WriteLine(string)
        IL_0010: nop
        IL_0011: ldloc.0
        IL_0012: ldc.i4.1
        IL_0013: add
        IL_0014: stloc.0
        IL_0015: nop
        IL_0016: ldloc.0
        IL_0017: ldc.i4.7
        IL_0018: clt
        IL_001a: stloc.1
        IL_001b: ldloc.1
        IL_001c: brtrue.s IL_0003
    // end loop
    IL_001e: ret
} // end of method Program::While

.method private hidebysig 
    instance void Goto () cil managed 
{
    // Method begins at RVA 0x207c
    // Code size 34 (0x22)
    .maxstack 2
    .locals init (
        [0] int32 x,
        [1] bool CS$4$0000
    )

    IL_0000: nop
    IL_0001: ldc.i4.0
    IL_0002: stloc.0
    // loop start (head: IL_0003)
        IL_0003: ldloca.s x
        IL_0005: call instance string [mscorlib]System.Int32::ToString()
        IL_000a: call void [mscorlib]System.Console::WriteLine(string)
        IL_000f: nop
        IL_0010: ldloc.0
        IL_0011: ldc.i4.1
        IL_0012: add
        IL_0013: stloc.0
        IL_0014: ldloc.0
        IL_0015: ldc.i4.7
        IL_0016: clt
        IL_0018: ldc.i4.0
        IL_0019: ceq
        IL_001b: stloc.1
        IL_001c: ldloc.1
        IL_001d: brtrue.s IL_0021

        IL_001f: br.s IL_0003
    // end loop

    IL_0021: ret
} // end of method Program::Goto

ILSpy事件将goto标记为循环。将其反编译为C#时,它甚至将两个循环显示为do while

但是有一个不同! while循环有一个范围:

        int x = 0;
        do
        {
            string z = "TEST";
            Console.WriteLine(x.ToString());
            ++x;
        }
        while (x < 7);
        Console.WriteLine(z); // invalid!

但这是有效的:

        int x = 0;
    label:
        string z = "TEST";
        Console.WriteLine(x.ToString());
        ++x;
        if (x < 7) goto label;
        Console.WriteLine(z);

你应该用吗?没有!请参阅Patricks回答。