我们为什么更喜欢? 至 ?? 运营商在C#?

我最近发现我们可以使用?? 操作员检查空值。 请检查下面的代码示例:

var res = data ?? new data(); 

这与之类似

  var res = (data==null) ? new data() : data ; 

我检查了我的整个项目源代码库和一些其他的开源项目。 而这?? 操作员从未使用过。

我只是想知道这背后有什么理由,比如性能问题还是什么的?

编辑:

我只是根据recursion和Anton的意见更新了示例代码。 这是一个粗心的错误。 🙁

空合并运算符在检查null时更清晰,这是它的主要目的。 它也可以被链接。

 object a = null; object b = null; object c = new object(); object d = a ?? b ?? c; //d == c. 

虽然该运算符仅限于空值检查,但三元运算符不是。 例如

 bool isQuestion = true; string question = isQuestion ? "Yes" : "No"; 

我认为人们并不知道空合并运算符,所以他们使用三元运算符。 三元语言在C#之前已经存在于大多数C语言的语言中,所以如果你不知道C#里面和外面和/或你编程在另一种语言,三元是一个自然的select。 如果你正在检查null,使用null coalesce操作符,它是为此而devise的,并且IL被稍微优化了(比较一下if和else)。

这是一个比较每个使用的例子

 object a = null; object b = null; object c = null; object nullCoalesce = a ?? b ?? c; object ternary = a != null ? a : b != null ? b : c; object ifThenElse; if (a != null) ifThenElse = a; else if (b != null) ifThenElse = b; else if (c != null) ifThenElse = c; 

首先,看看空的合并的语法,它是更清晰的。 三元组真是令人困惑。 现在让我们看看IL

仅空的合并

 .entrypoint .maxstack 2 .locals init ( [0] object a, [1] object b, [2] object c, [3] object nullCoalesce) L_0000: ldnull L_0001: stloc.0 L_0002: ldnull L_0003: stloc.1 L_0004: newobj instance void [mscorlib]System.Object::.ctor() L_0009: stloc.2 L_000a: ldloc.0 L_000b: dup L_000c: brtrue.s L_0015 L_000e: pop L_000f: ldloc.1 L_0010: dup L_0011: brtrue.s L_0015 L_0013: pop L_0014: ldloc.2 L_0015: stloc.3 L_0016: ldloc.3 L_0017: call void [mscorlib]System.Console::WriteLine(object) L_001c: ret 

只有三元

 .entrypoint .maxstack 2 .locals init ( [0] object a, [1] object b, [2] object c, [3] object ternary) L_0000: ldnull L_0001: stloc.0 L_0002: ldnull L_0003: stloc.1 L_0004: newobj instance void [mscorlib]System.Object::.ctor() L_0009: stloc.2 L_000a: ldloc.0 L_000b: brtrue.s L_0016 L_000d: ldloc.1 L_000e: brtrue.s L_0013 L_0010: ldloc.2 L_0011: br.s L_0017 L_0013: ldloc.1 L_0014: br.s L_0017 L_0016: ldloc.0 L_0017: stloc.3 L_0018: ldloc.3 L_0019: call void [mscorlib]System.Console::WriteLine(object) L_001e: ret 

如果那么只有

 .entrypoint .maxstack 1 .locals init ( [0] object a, [1] object b, [2] object c, [3] object ifThenElse) L_0000: ldnull L_0001: stloc.0 L_0002: ldnull L_0003: stloc.1 L_0004: newobj instance void [mscorlib]System.Object::.ctor() L_0009: stloc.2 L_000a: ldloc.0 L_000b: brfalse.s L_0011 L_000d: ldloc.0 L_000e: stloc.3 L_000f: br.s L_001a L_0011: ldloc.1 L_0012: brfalse.s L_0018 L_0014: ldloc.1 L_0015: stloc.3 L_0016: br.s L_001a L_0018: ldloc.2 L_0019: stloc.3 L_001a: ldloc.3 L_001b: call void [mscorlib]System.Console::WriteLine(object) L_0020: ret 

IL不是我的强项之一,所以也许有人可以编辑我的答案并扩展它。 我正要解释我的理论,但我宁愿不把自己和其他人混淆。 所有三个LOC的数量是相似的,但并不是所有的IL运营商都采取相同的时间长度来执行。

?? 运算符(也称为空合并运算符 )比三元运算符更为人所知,因为它使用.NET 2.0和可空types(Nullable Types)进行了首次亮相。 不使用它的原因可能包括不知道它存在,或者更熟悉三元运算符。

也就是说,检查null并不是三元运算符唯一的好处,所以它不是替代它,更像是一个更好的替代scheme,以满足特定的需求。 🙂

我能想到的一个原因是,这个操作符是在.NET 2.0中引入的,因此.NET 1.1的代码不能拥有它。

我同意你的看法,我们应该更频繁地使用它。

ref 链接

基于鲍勃的答案

 public object nullCoalesce(object a, object b, object c) { return a ?? b ?? c; } public object ternary(object a, object b, object c) { return a != null ? a : b != null ? b : c; } public object ifThenElse(object a, object b, object c) { if (a != null) return a; else if (b != null) return b; else return c; } 

…这是发布版本中的IL

 .method public hidebysig instance object nullCoalesce( object a, object b, object c) cil managed { .maxstack 8 L_0000: ldarg.1 L_0001: dup L_0002: brtrue.s L_000b L_0004: pop L_0005: ldarg.2 L_0006: dup L_0007: brtrue.s L_000b L_0009: pop L_000a: ldarg.3 L_000b: ret } .method public hidebysig instance object ternary( object a, object b, object c) cil managed { .maxstack 8 L_0000: ldarg.1 L_0001: brtrue.s L_000a L_0003: ldarg.2 L_0004: brtrue.s L_0008 L_0006: ldarg.3 L_0007: ret L_0008: ldarg.2 L_0009: ret L_000a: ldarg.1 L_000b: ret } .method public hidebysig instance object ifThenElse( object a, object b, object c) cil managed { .maxstack 8 L_0000: ldarg.1 L_0001: brfalse.s L_0005 L_0003: ldarg.1 L_0004: ret L_0005: ldarg.2 L_0006: brfalse.s L_000a L_0008: ldarg.2 L_0009: ret L_000a: ldarg.3 L_000b: ret } 

一个原因(如其他人已经触及)可能是缺乏意识。 也可以(就像我自己的情况一样),希望尽可能地保持代码库中类似事物的方法数量。 所以我倾向于使用三元运算符来处理所有紧凑的if-a-condition-is-met-do-this-otherwise-do-do-the-situation。

例如,我发现以下两个陈述在概念层面非常相似:

 return a == null ? string.Empty : a; return a > 0 ? a : 0; 

我认为这只是其他语言的习惯。 据我所知, ?? 运算符不用于任何其他语言。

我会想到相当于

 var res = data ?? data.toString(); 

将会

 var res = (data!=null) ? data : data.toString();