C# 去做与去做的区别
以下两个C#代码段在执行上有什么不同C# 去做与去做的区别,c#,loops,do-while,goto,C#,Loops,Do While,Goto,以下两个C#代码段在执行上有什么不同 do { Console.WriteLine(x.ToString()); ++x; } while (x < 7); do { Console.WriteLine(x.ToString()); ++x; } x
do
{
Console.WriteLine(x.ToString());
++x;
}
while (x < 7);
do
{
Console.WriteLine(x.ToString());
++x;
}
x<7;
及
标签:
{
Console.WriteLine(x.ToString());
++x;
}
如果(x<7)goto标签;
我想弄明白为什么goto这么糟糕。
谢谢
编辑:如果我添加括号,代码片段非常相似。
EDIT2:在VisualStudio中,我单击了“转到反汇编”,得到了以下代码:
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
do
{
00000037无
Console.WriteLine(x.ToString());
000000 38 lea ecx,[ebp-40h]
000000 3B呼叫63129C98
000000 40 mov德沃德ptr[ebp-48h],eax
000000 43 mov ecx,德沃德ptr[ebp-48h]
00000046致电63148168
0000004b无
++x;
0000004c公司dword ptr[ebp-40h]
}
0000004f无
x<7;
00000050 cmp dword ptr[ebp-40h],7
00000054 setl al
000000 57 movzx eax,al
000000 5A mov dword ptr[ebp-44h],eax
000000 5D cmp dword ptr[ebp-44h],0
00000061 jne 00000037
及
标签:
{
Console.WriteLine(x.ToString());
000000 69 lea ecx,[ebp-40h]
0000006c呼叫63129C98
000000 71 mov德沃德ptr[ebp-4Ch],eax
000000 74 mov ecx,德沃德ptr[ebp-4Ch]
00000077呼叫63148168
0000007c无
++x;
000000 7D公司dword ptr[ebp-40h]
}
00000080无
如果(x<7)goto标签;
000000 81 cmp dword ptr[ebp-40h],7
000000 85塞奇艾尔
000000 88 movzx eax,al
000000 8B mov dword ptr[ebp-44h],eax
000000 8E cmp dword ptr[ebp-44h],0
00000092 jne 00000097
00000094无
000000 95 jmp 000000 68
区别在于无条件跳转。不,我甚至认为
而
是像后面那样实现的
使用goto
的不好之处在于,它鼓励在代码中来回移动(也被称为“意大利面代码”:这是一团乱)。它使您的代码极难阅读、调试和分析,并引入了bug,因为您无法真正理解正在发生的事情
while
的好处在于,您可以理解它,编译器也可以理解它,因此它可以为您提供很好的警告。让我们看看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!
intx=0;
做
{
字符串z=“测试”;
Console.WriteLine(x.ToString());
++x;
}
x<7;
控制台。写入线(z);//无效的
但这是有效的:
int x = 0;
label:
string z = "TEST";
Console.WriteLine(x.ToString());
++x;
if (x < 7) goto label;
Console.WriteLine(z);
intx=0;
标签:
字符串z=“测试”;
Console.WriteLine(x.ToString());
++x;
如果(x<7)goto标签;
控制台写入线(z);
你应该用它吗?不请参阅答案。goto创建意大利面代码。只有少数情况下可以接受使用goto,例如跳出多个嵌套循环和在switch语句中跳出大小写。-这两个代码段的执行没有区别,但是当您需要编写比这更复杂的代码时,使用
goto
的代码变得很难维护。相关:是的,下面的代码段唯一的“问题”是未来的维护者、代码审阅者或您的老板会感到困惑“难道他们不知道而和其他标准语言结构吗?”——“我还能在他们的代码中找到什么其他的精华呢?”?“显然,这个问题不能从一个片段中看出。想象一下大型项目和goto在页面上来回跳跃..没错,但在某些情况下,而
循环是非常人工的,唯一的目的是避免goto
。这并不是说有不同的实现方法,真的。在某些情况下,您必须进行跳转(少数特殊情况除外,它们有自己的指令,如repnz
)。而
的另一个好处是,您有一个重复的显式块-很容易看出,它隔离了块的局部变量,总之,它可以防止您犯许多小错误。当然,它更能抵抗代码修改——很容易意外地中断goto
代码,尤其是在现在流行源代码管理的情况下(自动合并是一件痛苦的事情)。的确@Luaan。你应该防止使用goto
,并且只有在你真的需要的时候才使用(我从来没有见过这种情况,但还好)。
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);