C# 空合并、三元或If语句哪个工作得更快
我们使用空值对表达式求值,例如:C# 空合并、三元或If语句哪个工作得更快,c#,.net,null-coalescing-operator,C#,.net,Null Coalescing 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
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
而且
我知道空合并在语法上是精确的,但是在两者中哪一个编译得更快,执行得更快?为什么?它们都应该编译到相同的IL,从而执行相同的IL
即使没有,性能差异也可以忽略不计。我甚至不想看这个,除非我分析了我的应用程序并看到这些语句花费了大量的时间
始终使用最清晰的语法,除非您能够证明它导致了性能问题,而不是之前 你可能问错了问题。您不会选择使用一个而不是另一个,主要是因为效率(尽管这可能是次要问题),而是因为实用性 实际上,您应该将
?
与?:
进行比较,而不是将与进行比较,因为它们有不同的用途。是的,它们都是某种形式的“有条件的”善,但关键是?
和?:
都计算为一个值,而如果
没有,则它们通常有不同的用途
例如,以下代码:
Console.WriteLine("The order} is for {1} product",
orderId, productId ?? "every");
如果
,使用编写会更笨拙:
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
时计算一个值,而的目的是执行一个不同的路径(而不是直接产生一个值)
因此,一个更好的问题可能是,为什么不改为:
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_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: 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_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_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
(vs加载null
并与之比较)
因此所有这些都表明,你说的是IL方面的一个非常小的差异,这可能会影响性能,也可能不会影响性能。无论如何,与项目中更密集的工作(数学、数据库、网络等)相比,这可能会有非常小的差异
因此,我建议您选择可读性最好的方法,并且只有在您通过分析发现当前方法不足且存在瓶颈时才进行优化
对我来说,使用?:
或?
的真正原因是当您希望最终结果是一个值时。也就是说,任何时候您都可能想写:
if (someCondition)
x = value1;
else
x = value2;
然后我会使用条件(?:
),因为这是一个很好的缩写。x
根据这个条件获取一个或另一个值
然后,我将进一步讨论?
,并说同样的道理,您希望基于标识符的null
,为变量赋值
因此,如果
对流量控制很好,但是如果您只是返回两个值中的一个或根据条件分配两个值中的一个,我会使用?:
或?
,视情况而定
最后,请记住这些东西是如何实现的(IL和相关的性能)随着.NET Framework的每次修订都会发生变化(在我写这篇文章时,它们几乎可以忽略不计)
因此,今天可能更快的东西明天可能不会更快。所以,我还是要说,选择最合适的,你会发现最可读的
更新
顺便说一句,对于真正的痴迷者,我比较了上面每个代码样本的10000000次迭代,下面是执行每个代码样本的总时间。看起来?
对我来说是最快的,但同样地,这些都是非常接近于无关紧要的
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.
如果您非常关心运行时,C#可能不是您的最佳选择。如果这对您的代码造成了性能问题,您会有更大的担忧。第二个代码示例与第一个不同!如果foo
不是null
,第一个示例将foo
分配给foobar
。您的第二个示例是sn't.@comecme更糟糕的是:嵌入语句不能是声明或标记语句
奇怪的是,这被关闭为“不是一个真正的问题”,对我来说似乎是一个好问题。我确实认为“性能第一”通常是错误的策略,但在我的书中这仍然是一个有效的问题。我同意关于使用最清晰语法的部分,但是?
操作符实际上编译的IL更少。@JamesMichaelHare-真的吗?这很令人惊讶。不幸的是,我面前没有什么东西可以看……有什么区别吗?@JustinNiessner(禁用优化)@asawyer-您是否有可能发布一个采用参数而不是静态定义所有内容的方法的IL?看起来编译器可能已经优化了该IL。@JustinNiessner这看起来怎么样:我必须将foobar
赋值给一些值,正如我在上面的示例中所提到的。。因此,我在我的场景中使用了if
。使用我没有找到合适的方法。方法1和方法3是相同的(C代码)对不起,剪切粘贴错误,更正…@JamesMichaelHare答案非常详细,谢谢,真的很有帮助