首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >?.Invoke(x)是否与null-check +直接调用一样好?

?.Invoke(x)是否与null-check +直接调用一样好?
EN

Stack Overflow用户
提问于 2018-10-25 11:30:07
回答 1查看 330关注 0票数 0

当您有如下语句时,Visual会抱怨“可以简化委托调用”:

代码语言:javascript
复制
Action<int> foo = x;
if (foo != null)
    foo(10);

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

代码语言:javascript
复制
Action<int> foo = x;
foo?.Invoke(10);

编译器是否以一种很好的方式为您处理这个问题,并以任何一种方式生成相同的代码?还是后者的表现有所不同?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-10-25 12:45:41

在关闭优化的build中(通常是Debug ),您将得到以下两个IL指令序列:

代码语言:javascript
复制
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 

这里对于分支指令略有不同,但是让我们打开优化(通常是发布版本)进行构建:

代码语言:javascript
复制
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 

再一次,支路说明略有不同。具体来说,使用空合并运算符的示例将在堆栈上推送操作委托引用的副本,而带有if-语句的示例将使用临时局部变量。JITter可能会将两者都放入一个寄存器中,但是,这并不能确定它的行为是否会有所不同。

让我们尝试一些不同的东西:

代码语言:javascript
复制
public static void Action1(Action<int> foo)
{
    if (foo != null)
        foo(10);
}

public static void Action2(Action<int> foo)
{
    foo?.Invoke(10);
}

这将被编译(同样,在打开优化时)到:

代码语言:javascript
复制
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  

完全相同的代码。因此,上述例子中的不同之处在于与空合并操作符不同。

现在,为了回答您的具体问题,分支顺序是否会与您的示例影响性能?知道这一点的唯一方法是进行实际基准测试。然而,,如果结果是您需要考虑的东西,我会非常惊讶地。相反,我会根据您认为最容易编写、阅读和理解的代码风格来选择代码的样式。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52988237

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档