C# 返回变量是由编译器自动创建的吗?

C# 返回变量是由编译器自动创建的吗?,c#,compiler-construction,C#,Compiler Construction,在浏览项目源代码时,我偶然发现了一种方法,并对一件事感到好奇。从性能/内存/编译器的角度来看,以下两种方法完全相同吗 public static string Foo(string inputVar) { string bar = DoSomething(inputVar); return bar; } public static string Foo(string inputVar) { return DoSomething(inputVar); } 返回变量是由编

在浏览项目源代码时,我偶然发现了一种方法,并对一件事感到好奇。从性能/内存/编译器的角度来看,以下两种方法完全相同吗

public static string Foo(string inputVar)
{
    string bar = DoSomething(inputVar);
    return bar;
}

public static string Foo(string inputVar)
{
    return DoSomething(inputVar);
}
返回变量是由编译器自动创建的吗?

使用IL反汇编程序(包含在.NET SDK/VS中),您可以查看编译器生成的IL。代码是使用VS2013(而不是Roslyn)生成的

顶部给出了以下IL:

.method public hidebysig static string  Foo(string inputVar) cil managed
{
  // Code size       14 (0xe)
  .maxstack  1
  .locals init ([0] string bar,
           [1] string CS$1$0000)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  call       string TestIL.Program::DoSomething(string)
  IL_0007:  stloc.0
  IL_0008:  ldloc.0
  IL_0009:  stloc.1
  IL_000a:  br.s       IL_000c
  IL_000c:  ldloc.1
  IL_000d:  ret
} // end of method Program::Foo
第二条:

.method public hidebysig static string  Foo(string inputVar) cil managed
{
  // Code size       12 (0xc)
  .maxstack  1
  .locals init ([0] string CS$1$0000)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  call       string TestIL.Program::DoSomething(string)
  IL_0007:  stloc.0
  IL_0008:  br.s       IL_000a
  IL_000a:  ldloc.0
  IL_000b:  ret
} // end of method Program::Foo
区别似乎是第一个在methods locals表中创建了一个额外的条目。我不知道这是不是被JIT编译器优化了

回答这个问题:不,在这种情况下,编译器似乎不会自动生成局部变量,但在更高级的情况下,它可能会生成局部变量(如返回x*(y+z))

编辑:如果启用“优化代码”,则更清楚:

.method public hidebysig static string  Foo(string inputVar) cil managed
{
  // Code size       9 (0x9)
  .maxstack  1
  .locals init ([0] string bar)
  IL_0000:  ldarg.0
  IL_0001:  call       string TestIL.Program::DoSomething(string)
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ret
} // end of method Program::Foo

.method public hidebysig static string  Foo(string inputVar) cil managed
{
  // Code size       7 (0x7)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  call       string TestIL.Program::DoSomething(string)
  IL_0006:  ret
} // end of method Program::Foo

如果这是您的实际性能瓶颈,恭喜您。然而,我的经验表明这不太可能。编写您认为最清晰的代码,在性能问题得到实际验证时担心性能,并确定在哪里最好地投资您的资源。@Damien_不相信者:这不是关于性能的问题。只是一个关于.Net/C#如何工作的简单问题。@NikhilAgrawal-所以问题中提到的性能和被标记为性能的问题应该被忽略?@Damien#不相信,这是一个关于性能和C#如何工作的问题。创建问题时,我假设非常清楚,
bar
的类型被指定为string作为示例。它也可能是一种需要大量内存的复杂类型。祝贺你。考虑打开编译器优化。我预计它将把IL分别削减到3到5条指令,并使差异更加明显。从表面上看,剩余的
stloc
/
ldloc
对看起来是微不足道的优化。关于为什么乐观主义者没有这样做,有什么线索吗?或者,想想看,
.maxstack 8
有什么影响?这只是一个猜测:这是因为大部分优化都推迟到运行时,而JIT编译器正在进行真正的优化。为了让JIT编译器做出最佳决策,编译器会跳过一些优化,以便JIT编译器更好地理解程序。从技术上讲,只有在需要从不允许从try块返回ret的位置执行返回时,编译器才需要添加一个返回本地。在这种情况下,返回值需要在跳转到出口标签之前溢出到temp中,在出口标签中可以返回temp中的值。通常情况下,返回局部是不必要的,优化的codegen将尝试避免它。不过也不太难-一个额外的本地不是什么大问题。@ClickRick stlock/ldlock确实是多余的,Roslyn将在使用/o+编译时对其进行优化。请注意,在调试优化代码时,您将无法停止“}”并检查“bar”。maxstack 8只是因为该方法使用了微不足道的“微小”头,这意味着“没有局部变量,没有异常帧,maxstack”