哪个工作更快Null coalesce,Ternary或If Statement

时间:2012-11-14 19:01:27

标签: c# .net null-coalescing-operator

我们使用?? Operator来计算针对空值的表达式,例如:

string foo = null;
string bar = "woooo";
string foobar= foo ?? bar ; 
// Evaluates foobar as woooo

我们还使用了if语句,如果与上面的表达式

一起使用,它的工作方式相同
string foo = null;
string bar = "woooo";
if(foo==null)
   string foobar=   "woooo" ;
// Evaluates foobar as woooo same as above

还有?: Ternary Operator ......

string foo = null;
string bar = "woooo";    
string foobar= foo==null ? "woooo" : null ;
// Evaluates foobar as woooo same as above

我知道空合并在语法上是精确的,但哪一个在两者之间编译得更快,执行速度更快,为什么?

2 个答案:

答案 0 :(得分:18)

你可能会问错误的问题。您不会选择使用一个而不是主要由于效率(尽管可能是次要问题),但由于效用。

你真的应该将???:进行比较,而不是if进行比较,因为它们有不同的用途。是的,它们都是某种形式的“有条件”善良,但关键是???:都评估为一个值,而if则没有,因此它们通常有不同的用途

例如,以下代码:

Console.WriteLine("The order} is for {1} product",
    orderId, productId ?? "every");

使用if

会更笨拙
if (productId == null)
{
    Console.WriteLine("The order {0} is for every product",
        orderId);
}
else
{
    Console.WriteLine("The order {0} is for {1} product",
        orderId, productId);
}

是的,你可以压缩到一个,但是你有一个临时变量等等:

if (productId == null)
{
    productId = "every";
}

Console.WriteLine("The order {0} is for {1} product",
    orderId, productId);

因此,你真的不应该比较两者,因为如果参数是??null的点是评估值,而if的点是执行不同的路径(不直接产生值。

所以,一个更好的问题可能是,为什么不这样做呢:

Console.WriteLine("The order {0} is for {1} product",
    orderId, productId == null ? "every" : productId);

它们大致相同(都评估为一个值)并且没有流量控制那么多。

所以,让我们看看差异。让我们用三种方式编写这段代码:

// Way 1 with if
string foo = null;
string folder = foo;

if (folder == null)
{
    folder = "bar";
}

// Way 2 with ? :
string foo2 = null;
var folder2 = foo2 != null ? foo2 : "bar";

// Way 3 with ??
string foo3 = null;
var folder3 = foo3 ?? "bar";

对于IF,我们得到以下IL:

IL_0001:  ldnull      
IL_0002:  stloc.0     
IL_0003:  ldloc.0     
IL_0004:  ldnull      
IL_0005:  ceq         
IL_0007:  ldc.i4.0    
IL_0008:  ceq         
IL_000A:  stloc.1     
IL_000B:  ldloc.1     
IL_000C:  brtrue.s    IL_0016
IL_000E:  nop         
IL_000F:  ldstr       "bar"
IL_0014:  stloc.0     

对于条件(?:),我们得到以下IL:

IL_0001:  ldnull      
IL_0002:  stloc.0     
IL_0003:  ldloc.0     
IL_0004:  brtrue.s    IL_000D
IL_0006:  ldstr       "bar"
IL_000B:  br.s        IL_000E
IL_000D:  ldloc.0     
IL_000E:  nop         
IL_000F:  stloc.1   

对于null-coallescing(??),我们得到这个IL:

IL_0001:  ldnull      
IL_0002:  stloc.0     
IL_0003:  ldloc.0     
IL_0004:  dup         
IL_0005:  brtrue.s    IL_000D
IL_0007:  pop         
IL_0008:  ldstr       "bar"
IL_000D:  stloc.1    

请注意每个连续的更简单? if是更大的IL,因为它需要分支逻辑来处理单独的语句。 ?:较小,因为它只是求值为一个值(不分支到其他语句),但仍需要加载操作数以与(null)进行比较。

??是最简单的,因为有一条IL指令用于与null(与加载null进行比较并与之进行比较。

SO 所有这些都说,你说IL的差异很小,可能会影响性能,也可能不会影响性能。无论如何,与程序中更密集的工作(数学,数据库,网络等)相比,这可能会有非常的重大差异。

因此,我建议选择 most 可读的那个,并且只有在通过分析找出当前方法不充分且瓶颈时才进行优化。

对我而言,使用?:??的真正原因是您希望最终结果为值。也就是说,任何时候你都想写作:

if (someCondition)
    x = value1;
else 
    x = value2;

然后我会使用条件(?:),因为这是一个很好的简写。 x根据此条件获取一个或另一个值...

然后我会再次使用??并说同样如此,你想根据一个标识符的null来为变量赋值。

所以if非常适合流量控制,但如果您只是返回两个值中的一个或根据条件分配两个值中的一个,我会使用?:或{{1酌情。

最后请记住这些内容如何在幕后实现(IL和相关性能)随.NET Framework的每个版本而变化(从我现在开始写这些内容时)都是如此接近可以忽略不计)。

因此,今天可能更快的可能明天不会更快。再说一次,我只想说最合适的一个,你发现最可读。

<强>更新

顺便提一下,对于真正的痴迷者,我比较了上面每个代码样本的10,000,000次迭代,这里是执行每个代码样本的总时间。看起来??对我来说速度最快,但这些所以接近几乎无关紧要......

??

答案 1 :(得分:5)

他们应该编译到同一个IL,然后执行相同的。

即使他们没有,性能差异也可以忽略不计。除非我查看我的申请并且看到这些陈述需要花费大量时间,否则我甚至不会考虑这个。

始终使用最清晰的语法,除非您能证明它导致性能问题,而不是之前。

相关问题