为什么我们更喜欢?至 ??c#中的运算符?

Ram*_*Vel 39 c# conditional-operator

我最近发现我们可以使用?? 运算符检查空值.请检查以下代码示例:

   var res = data ?? new data();
Run Code Online (Sandbox Code Playgroud)

这与之完全相似

   var res = (data==null) ? new data() : data ;
Run Code Online (Sandbox Code Playgroud)

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

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

编辑:

我刚刚根据recursive和Anton的评论更新了我的示例代码.粗心大意是一个错误.:(

Bob*_*Bob 58

检查null时,null coalesce运算符更清晰,这是它的主要目的.它也可以链接.

object a = null;
object b = null;
object c = new object();
object d = a ?? b ?? c; //d == c.
Run Code Online (Sandbox Code Playgroud)

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

bool isQuestion = true;
string question = isQuestion ? "Yes" : "No";
Run Code Online (Sandbox Code Playgroud)

我认为人们只是不知道null coalesce运算符,所以他们使用三元运算符代替.在大多数C风格语言中C#之前存在三元组,所以如果你不知道C#里面和外面和/或你用另一种语言编程,那么三元是一个自然的选择.但是,如果要检查null,请使用null coalesce运算符,它是为此设计的,并且IL稍微优化(比较?? if 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;
Run Code Online (Sandbox Code Playgroud)

首先,看一下null coalesce的语法,它更清晰.三元真的很混乱.现在让我们看一下IL

Null Coalesce Only

.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 
Run Code Online (Sandbox Code Playgroud)

仅三元

.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 
Run Code Online (Sandbox Code Playgroud)

如果那么只有

.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 
Run Code Online (Sandbox Code Playgroud)

IL不是我的优点之一,所以也许有人可以编辑我的答案并扩展它.我打算解释我的理论,但我宁愿不要把自己和别人搞混.所有三个LOC的LOC数相似,但并非所有IL运算符都需要相同的执行时间.

  • 它不是*三元运算符,它只是*一个*三元运算符.你所指的是**条件运算符**. (8认同)
  • @divo:它是C#中唯一的三元运算符:P (3认同)
  • 好吧,我是一个挑剔的人.条件运算符是C#引用中使用的官方术语;) (2认同)
  • 我自己做了,而nullCoalesce确实做了最优化的版本.我将发布我的代码作为另一个答案. (2认同)

Ryt*_*mis 12

?? ?? 运算符(也称为null-coalescing运算符)比三元运算符更为人所知,因为它首次使用.NET 2.0和Nullable Types.不使用它的原因可能包括没有意识到它存在,或者更熟悉三元运算符.

也就是说,检查null并不是三元运算符唯一有用的东西,所以它不是它的替代品,更像是一个非常特殊需求的更好选择.:)


Bin*_*ony 6

我能想到的一个原因是这个运算符是在.NET 2.0中引入的,所以.NET 1.1的代码不能拥有它.

我同意你的观点,我们应该更频繁地使用它.

ref 链接