你可能问错了问题。您不会选择使用其中一个而不是另一个主要是因为效率(尽管它可能是次要问题),而是由于实用性。
真的,您应该比较而不是比较,??
因为它们有不同的目的。是的,它们都是某种形式的“有条件的”善,但关键是两者都评估为一个值,而不是,因此它们通常有不同的用途。?:
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
对于空合并 (??) 我们得到这个 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
注意每个连续的如何更简单?IL 更大,if
因为它需要分支逻辑来处理单独的语句。更小,?:
因为它只是计算一个值(不分支到其他语句),但仍需要加载操作数以与 ( null
) 进行比较。
这??
是最简单的,因为有一个 IL 指令用于比较null
(vs 加载null
和比较它。
所以所有这一切都说,你说的是 IL 方面的一个非常小的差异,这可能会或可能不会影响性能。无论如何,与程序中更密集的工作(数学、数据库、网络等)相比,这很可能没有太大的区别。
因此,我建议选择最易读的一个,并且仅在通过分析发现当前方法不足且存在瓶颈时才进行优化。
对我来说,使用?:
or的真正原因??
是当您希望最终结果成为一个值时。也就是说,任何时候你都想写:
if (someCondition)
x = value1;
else
x = value2;
然后我会使用条件 ( ?:
),因为这是一个很好的速记。 x
根据此条件获取一个或另一个值...
然后我会更进一步,说同样是真的,你想根据标识符的 -ness??
为变量分配一个值。null
所以if
非常适合流量控制,但是如果您只是返回两个值之一或根据条件分配两个值之一,我会使用?:
或??
酌情使用。
最后请记住,这些东西是如何在幕后实现的(IL 和相关的性能)会随着 .NET Framework 的每次修订而发生变化(在我现在写这篇文章时,它们都非常接近可以忽略不计)。
因此,今天可能更快的东西明天可能不会更快。再说一次,我只想说选择最合适的那个,你会发现它最易读。
更新
顺便说一句,对于真正痴迷的人,我比较了上面每个代码样本的 10,000,000 次迭代,这是执行每个代码的总时间。看起来??
对我来说是最快的,但这些又是如此接近以至于几乎无关紧要......
10,000,000 iterations of:
?: took: 489 ms, 4.89E-06 ms/item.
?? took: 458 ms, 4.58E-06 ms/item.
if took: 641 ms, 6.41E-06 ms/item.