代码之家  ›  专栏  ›  技术社区  ›  Rob Pranay Rana

.Invoke(x)是否执行空检查+直接调用

  •  0
  • Rob Pranay Rana  · 技术社区  · 7 年前

    当您有这样的语句时,Visual Studio会抱怨“委托调用可以简化”:

    Action<int> foo = x;
    if (foo != null)
        foo(10);
    

    快速操作智能标记规则希望您将此更改为:

    Action<int> foo = x;
    foo?.Invoke(10);
    

    1 回复  |  直到 7 年前
        1
  •  3
  •   Lasse V. Karlsen    7 年前

    在优化关闭的内部版本中(通常是调试内部版本),您将获得以下两个IL指令序列:

    IL_0000:  nop                               IL_0000:  nop         
    IL_0001:  ldnull                            IL_0001:  ldnull      
    IL_0002:  ldftn       x                     IL_0002:  ldftn       x
    IL_0008:  newobj      Action<int>..ctor     IL_0008:  newobj      Action<int>..ctor
    IL_000D:  stloc.0     // foo                IL_000D:  stloc.0     // foo
    IL_000E:  ldloc.0     // foo                IL_000E:  ldloc.0     // foo
    IL_000F:  ldnull                            IL_000F:  brtrue.s    IL_0013
    IL_0010:  cgt.un                            IL_0011:  br.s        IL_001C
    IL_0012:  stloc.1     
    IL_0013:  ldloc.1     
    IL_0014:  brfalse.s   IL_001F
    IL_0016:  ldloc.0     // foo                IL_0013:  ldloc.0     // foo
    IL_0017:  ldc.i4.s    0A                    IL_0014:  ldc.i4.s    0A 
    IL_0019:  callvirt    Action<int>.Invoke    IL_0016:  callvirt    Action<int>.Invoke
    IL_001E:  nop                               IL_001B:  nop         
    IL_001F:  ret                               IL_001C:  ret 
    

    关于分支指令,这里有一些细微的差别,但是让我们在启用优化的情况下进行构建(通常是发布构建):

    IL_0000:  ldnull                            IL_0000:  ldnull      
    IL_0001:  ldftn       x                     IL_0001:  ldftn       x
    IL_0007:  newobj      Action<int>..ctor     IL_0007:  newobj      Action<int>..ctor
    IL_000C:  stloc.0     // foo                IL_000C:  dup         
    IL_000D:  ldloc.0     // foo                IL_000D:  brtrue.s    IL_0011
    IL_000E:  brfalse.s   IL_0018               IL_000F:  pop         
    IL_0010:  ldloc.0     // foo                IL_0010:  ret         
    IL_0011:  ldc.i4.s    0A                    IL_0011:  ldc.i4.s    0A 
    IL_0013:  callvirt    Action<int>.Invoke    IL_0013:  callvirt    Action<int>.Invoke
    IL_0018:  ret                               IL_0018:  ret 
    

    让我们尝试不同的方法:

    public static void Action1(Action<int> foo)
    {
        if (foo != null)
            foo(10);
    }
    
    public static void Action2(Action<int> foo)
    {
        foo?.Invoke(10);
    }
    

    这将被编译(同样,在启用优化的情况下)为:

    IL_0000:  ldarg.0                           IL_0000:  ldarg.0     
    IL_0001:  brfalse.s   IL_000B               IL_0001:  brfalse.s   IL_000B
    IL_0003:  ldarg.0                           IL_0003:  ldarg.0     
    IL_0004:  ldc.i4.s    0A                    IL_0004:  ldc.i4.s    0A 
    IL_0006:  callvirt    Action<int>.Invoke    IL_0006:  callvirt    Action<int>.Invoke
    IL_000B:  ret                               IL_000B:  ret  
    

    完全相同的代码

    要知道这一点,唯一的方法就是实际进行基准测试。 ,我会的 非常 如果这是你需要考虑的事情,你会很惊讶。相反,我会根据您认为最容易编写、阅读和理解的内容来选择代码的样式。

    推荐文章