问题 c#编译器比VB.NET编译器更智能吗?


如果我查看Linqpad中为以下两个代码片段创建的IL,我想知道这里发生了什么。

在c#中

int i = 42;

得到以下IL代码

IL_0000:  ret

而在VB中

Dim i As Integer = 42

它是

IL_0000:  ldc.i4.s    2A 
IL_0002:  stloc.0  

显然,c#编译器理解该值从未使用过,因此根本不返回任何内容。在VB.NET中,实际代码被翻译。

这是由于编译器优化的差异还是还有其他工作原因?

更新: 只是为了澄清这一点 - 我只是将这一行输入LinqPad并查看它创建的IL(最明确的是运行相应的编译器)。那里 没有 程序。


5615
2018-02-09 10:55


起源

我相信C#编译器目前执行更多优化(当Roslyn进入时几乎肯定会改变)。但在这种情况下,它对于C#编译器来说是一个错误的胜利,因为它几乎肯定会被JIT编译器优化。 - Damien_The_Unbeliever
@AakashM:在我写的时候,这就是我进入LinqPad的全部内容。没有其他代码。 LinqPad通过在后台编译生成IL。 - Olaf
在调试中它是否在C#中编译相同?我假设C#在发布模式下比VB.NET更优化。这就是为什么你不能在C#中发布可以在VB.NET中使用时未使用的变量的原因之一。我更喜欢后者。 - Tim Schmelter
@Tim:我根本就没有编译 - 这只是一个LinqPad IL的结果。 - Olaf
LINQPad调用CodeDomProviders用于C#和VB,它们会破坏CSC.exe和VB编译器。因此,IL翻译的保真度。您可以在编辑|中启用/禁用编译器优化首选项(或最近测试版状态栏上的按钮)。 - Joe Albahari


答案:


拿走linqpad问题,我跑了 vbc 和 csc 同 /optimize+ /debug- 在这些计划上:

Module f

Public Sub Main()
    Dim i As Integer = 42
End Sub

End Module

public static class f
{

public static void Main()
{
    int i = 42;
}

}

并从ILDASM获得这些CIL结果:

对于VB:

.method public static void  Main() cil managed
{
  .entrypoint
  .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       4 (0x4)
  .maxstack  1
  .locals init (int32 V_0)
  IL_0000:  ldc.i4.s   42
  IL_0002:  stloc.0
  IL_0003:  ret
} // end of method f::Main

对于C#:

.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       1 (0x1)
  .maxstack  8
  IL_0000:  ret
} // end of method f::Main

所以, ,至少在这方面 csc 比'聪明' vbc。但我敢打赌,JITter会在执行时删除任何差异。

编辑

我检查了,实际上是执行的本机代码  不同,至少在我的系统上。我投入 Console.ReadLine() 调用两者给我一个附加调试器的机会,我得到了这些反汇编:

来自VB:

00000000  sub         rsp,38h 
00000004  mov         dword ptr [rsp+20h],0 
0000000c  mov         rax,7FF000434D8h 
00000016  mov         eax,dword ptr [rax] 
00000018  test        eax,eax 
0000001a  je          0000000000000021 
0000001c  call        FFFFFFFFE45BA230 
00000021  mov         dword ptr [rsp+20h],2Ah 
00000029  call        FFFFFFFFE26ABF20 
0000002e  mov         qword ptr [rsp+28h],rax 
00000033  nop 
00000034  jmp         0000000000000036 
00000036  add         rsp,38h 
0000003a  ret 

来自C#:

00000000  sub         rsp,38h 
00000004  mov         rax,7FF000534D8h 
0000000e  mov         eax,dword ptr [rax] 
00000010  test        eax,eax 
00000012  je          0000000000000019 
00000014  call        FFFFFFFFE45AA230 
00000019  call        FFFFFFFFE391BF20 
0000001e  mov         qword ptr [rsp+20h],rax 
00000023  nop 
00000024  jmp         0000000000000026 
00000026  add         rsp,38h 
0000002a  ret 

现在,我的组合几乎不存在,但即使我可以看到

mov         dword ptr [rsp+20h],2Ah 

在from-VB中指的是十六进制的常量值 2A,这是小数点后42。所以,你去吧  最后执行更多指令。


10
2018-02-09 11:10



好吧,我们也有一个更大的堆栈,如8(C#)vs 1 - V4Vendetta


我认为在C#情况下,编译器执行内存分配部分并在一个步骤中保存int的值,在VB情况下,DIM语句是第一步,它只分配内存,然后将值保存在第二步。 DIM是通用的,并且与所有数据类型一起使用,因此在所有情况下它可能都是额外的一步。思考?


1
2018-02-09 11:29