如何编译返回值的方法调用?

时间:2012-02-25 18:46:49

标签: c# .net compiler-construction

如果我有一个返回值的方法(例如Remove类的Dictionary方法返回bool),如果我没有将返回值赋给a {变量?换句话说,如果我在没有将结果分配给dictionary.Remove("plugin-01");变量的情况下编写bool,那么编译与bool b = dictionary.Remove("plugin-01");的区别是什么?

3 个答案:

答案 0 :(得分:15)

让我们看一个简单的例子和​​它产生的IL代码(由LinqPad提供)

void Main()
{
    Bar();
    Baz();
}

bool Bar()
{
  return true;
}

void Baz()
{
  Console.WriteLine("placeholder");
}

这产生以下IL:

IL_0000:  ldarg.0     
IL_0001:  call        UserQuery.Bar
IL_0006:  pop          //remove current value from evaluation stack
IL_0007:  ldarg.0     
IL_0008:  call        UserQuery.Baz


Bar:
IL_0000:  ldc.i4.1    
IL_0001:  ret    

Baz:
IL_0000:  ldstr       "placeholder"
IL_0005:  call        System.Console.WriteLine
IL_000A:  ret   

你可以看到Bar被调用,然后一个pop来从评估堆栈中删除布尔返回值 - 它无处可去。我必须更新示例以包含对Baz()的另一个方法调用,否则不会发出pop,因为程序结束。

现在让我们看一下我们实际使用返回值的情况:

void Main()
{
    bool foo = Bar();
    Console.WriteLine(foo);
}

bool Bar()
{
  return true;
}

这产生以下IL:

IL_0000:  ldarg.0     
IL_0001:  call        UserQuery.Bar
IL_0006:  stloc.0    //pops current value from evaluation stack, stores in local var
IL_0007:  ldloc.0     
IL_0008:  call        System.Console.WriteLine

Bar:
IL_0000:  ldc.i4.1    
IL_0001:  ret 

忽略System.Console.WriteLine部分,包括IL_007之后的所有部分 - 只需添加它,这样编译器就不会优化变量的使用。您会看到Bar方法调用的结果从评估堆栈中弹出并存储在局部变量foo中。这就是区别 - 要么抓取并删除返回值的pop,要么将stloc.0分配给变量。

因此,如果您不需要方法调用的结果,则应该忽略结果。即使您将结果分配给变量并且该变量从未使用过,编译器也可能完全优化掉变量和赋值 - 至少在发布模式下(在调试模式下,大多数优化都被禁用以改善您的调试体验)。 p>

答案 1 :(得分:3)

如果你不使用它,那么简单地会忽略返回值。

您可以执行类似

的操作
if(!dictionary.Remove("plugin-01")) {
    MessageBox.Show("Error: plugin-01 does not exist!");
}

如果你不在乎,你可以安全地写

dictionary.Remove("plugin-01");

答案 2 :(得分:1)

调用部分相同,但在IL中跳过赋值部分。看一看 - 这是一个简单程序的反汇编:

var d = new Dictionary<int,int>();
bool a = d.Remove(5);
d.Remove(6);

反汇编看起来像这样:

    bool a = d.Remove(5);
00000057  mov         ecx,dword ptr [ebp-40h] 
0000005a  mov         edx,5 
0000005f  cmp         dword ptr [ecx],ecx 
00000061  call        69106A00
// Here is the assignment part 
00000066  mov         dword ptr [ebp-4Ch],eax 
00000069  movzx       eax,byte ptr [ebp-4Ch] 
0000006d  mov         dword ptr [ebp-44h],eax 
    d.Remove(6);
00000070  mov         ecx,dword ptr [ebp-40h] 
00000073  mov         edx,6 
00000078  cmp         dword ptr [ecx],ecx 
0000007a  call        69106A00

前四行是常见的;第一次通话的最后三行处理作业;在第二次通话的反汇编中它们丢失了。