我的理解是这两个命令最终的结果相同,即将X增加1,但后者可能更有效率。
如果我理解有误,请解释一下它们的区别。
如果我理解正确,为什么后者更有效率?难道它们不应该编译成相同的IL代码吗?
谢谢。
我的理解是这两个命令最终的结果相同,即将X增加1,但后者可能更有效率。
如果我理解有误,请解释一下它们的区别。
如果我理解正确,为什么后者更有效率?难道它们不应该编译成相同的IL代码吗?
谢谢。
从MSDN +=库中得知:
使用此运算符几乎与指定结果 = result + expression相同,唯一的区别是result只被计算一次。
因此,它们并不完全相同,这就是为什么 x += 1 更有效率的原因。
更新:我刚刚注意到我的MSDN库链接指向JScript页面而不是VB页面,后者不包含相同的引用。
因此,经过进一步的研究和测试,该答案不适用于VB.NET。我错了。这里是一个示例控制台应用程序:
Module Module1
Sub Main()
Dim x = 0
Console.WriteLine(PlusEqual1(x))
Console.WriteLine(Add1(x))
Console.WriteLine(PlusEqual2(x))
Console.WriteLine(Add2(x))
Console.ReadLine()
End Sub
Public Function PlusEqual1(ByVal x As Integer) As Integer
x += 1
Return x
End Function
Public Function Add1(ByVal x As Integer) As Integer
x = x + 1
Return x
End Function
Public Function PlusEqual2(ByVal x As Integer) As Integer
x += 2
Return x
End Function
Public Function Add2(ByVal x As Integer) As Integer
x = x + 2
Return x
End Function
End Module
对于PlusEqual1和Add1来说,它们的IL确实是相同的:
.method public static int32 Add1(int32 x) cil managed
{
.maxstack 2
.locals init (
[0] int32 Add1)
L_0000: nop
L_0001: ldarg.0
L_0002: ldc.i4.1
L_0003: add.ovf
L_0004: starg.s x
L_0006: ldarg.0
L_0007: stloc.0
L_0008: br.s L_000a
L_000a: ldloc.0
L_000b: ret
}
对于PlusEqual2和Add2,它们的IL几乎完全相同:
.method public static int32 Add2(int32 x) cil managed
{
.maxstack 2
.locals init (
[0] int32 Add2)
L_0000: nop
L_0001: ldarg.0
L_0002: ldc.i4.2
L_0003: add.ovf
L_0004: starg.s x
L_0006: ldarg.0
L_0007: stloc.0
L_0008: br.s L_000a
L_000a: ldloc.0
L_000b: ret
}
我写了一个简单的控制台应用:
static void Main(string[] args)
{
int i = 0;
i += 1;
i = i + 1;
Console.WriteLine(i);
}
我使用 Reflector 进行了反汇编,以下是我得到的内容:
private static void Main(string[] args)
{
int i = 0;
i++;
i++;
Console.WriteLine(i);
}
它们是相同的。
它们编译成相同的结果,第二种方式只是更容易打出来。
对于一般的语言来说,指定评估的答案在解释 +=
的作用方面是正确的。但在VB.NET中,我假设OP中指定的X
是一个变量或属性。
它们很可能编译成相同的IL代码。
VB.NET是一种编程语言的规范。任何符合规范定义的编译器都可以成为VB.NET实现。如果您编辑MS VB.NET编译器的源代码以生成X += 1
的低效代码,您仍将符合VB.NET规范(因为它没有说明它将如何工作,只是说效果将完全相同,这使得生成相同的代码是合理的)。
虽然编译器非常非常可能(我确实这样认为)为两者生成相同的代码,但它是相当复杂的软件。事实上,即使编译相同的代码两次,你也不能保证编译器会生成完全相同的代码!
您可以100%放心地说(除非您非常熟悉编译器的源代码),好的编译器应该生成性能相同的代码,这可能是完全相同的代码,也可能不是。
有太多的猜测了!即使是关于反射器的结论也未必正确,因为它可以在反汇编时进行优化。
那么为什么你们中没有人看一下IL代码呢?看看下面的C#程序:
static void Main(string[] args)
{
int x = 2;
int y = 3;
x += 1;
y = y + 1;
Console.WriteLine(x);
Console.WriteLine(y);
}
这段代码会编译成以下IL代码:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 25 (0x19)
.maxstack 2
.locals init ([0] int32 x,
[1] int32 y)
// 这里省略了一些指令
IL_0004: ldloc.0
IL_0005: ldc.i4.1
IL_0006: add
IL_0007: stloc.0
IL_0008: ldloc.1
IL_0009: ldc.i4.1
IL_000a: add
IL_000b: stloc.1
// 这里省略了一些指令
}
可以看到,实际上是完全一样的。因为IL的作用是告诉计算机要做什么,而不是怎么做。优化的工作将由JIT编译器完成。顺便提一下,VB.Net也是一样的。
在x86平台上,如果寄存器eax保存了变量x,那么下面两行代码都会产生类似的结果:
inc eax;
所以你是对的,在某些编译阶段后,中间语言(IL)将是相同的。
还有一系列类似的问题可以用"信任你的优化器"来回答。
一个著名的谬论是:
x++;
比
++x;
效率低,因为它需要存储一个临时值。但是如果你从不使用这个临时值,优化器会删除该存储操作。
这两个内容是相同的。
x=x+1
x+=1
不是和是轻松打字。
在VB中它们可能是相同的;但在C语言中它们不一定相同(因为该操作符起源于C语言)。
如果x是一个简单类型,如int或float,则优化器可能会产生相同的结果。
如果您使用了其他语言(对VB知识有限,您可以重载+=吗?),其中x可以是一个大型对象,前者将创建一个额外的副本,可能会占用数百兆字节。后者则不会。