【问题标题】:why do we prefer ? to ?? operator in c#?为什么我们更喜欢?到 ?? c#中的运算符?
【发布时间】:2010-12-05 08:06:10
【问题描述】:

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

   var res = data ?? new data();

这与

完全相似
   var res = (data==null) ? new data() : data ;

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

我只是想知道这背后是否有任何原因,比如性能问题或其他什么?

编辑:

我刚刚根据 recursive & Anton 的 cmets 更新了我的示例代码。这是粗心的错误。 :(

【问题讨论】:

  • 一个原因是人们可能不知道!
  • 应该是“var res = (data!=null) ? data : new data();”在您的样本中
  • @Rubens,只是傻而且不对。

标签: c# conditional-operator


【解决方案1】:

null 合并运算符在检查 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# 的内里和外在和/或你用另一种语言编程,那么三进制是一个自然的选择。但是,如果您要检查 null,请使用 null coalesce 运算符,它是为此而设计的,并且 IL 进行了轻微优化(将 ?? 与 if then 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 运算符都需要相同的时间长度来执行。

【讨论】:

  • 这不是 三元运算符,它只是 one 三元运算符。您所指的称为条件运算符
  • @Bob,这是一个很好的比较......当我们处理空值时,coalesce 运算符比所有运算符都要好......
  • @divo:嗯,它是 C# 中唯一的三元运算符:P
  • 好吧,我是个吹毛求疵的人。条件运算符是 C# 参考中使用的官方术语;)
  • 我自己做的,nullCoalesce 确实是最优化的版本。我将发布我的代码作为另一个答案。
【解决方案2】:

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

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

参考link

【讨论】:

    【解决方案3】:

    ??运算符(也称为null-coalescing operator)比三元运算符鲜为人知,因为它在 .NET 2.0 和 Nullable 类型中首次亮相。不使用它的原因可能包括不知道它的存在,或者更熟悉三元运算符。

    也就是说,检查 null 并不是三元运算符的唯一优点,因此它不能替代它,更像是满足特定需求的更好选择。 :)

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 我知道这是超级旧的,但我想评论说它也在 Perl v5.10 中使用,只有// 而不是??。一个例子(取自wikipedia):$possibly_null_value // $value_if_null
      【解决方案5】:

      我会认为相当于

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

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

      【讨论】:

        【解决方案6】:

        一个原因(正如其他人已经触及的)可能是缺乏意识。它也可能是(就像我自己的情况一样),希望尽可能减少在代码库中做类似事情的方法的数量。因此,我倾向于将三元运算符用于所有紧凑的 if-a-condition-is-met-do-this-otherwise-do-that 情况。

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

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

        【讨论】:

          【解决方案7】:

          基于Bob's的回答

          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 
          }
          

          【讨论】:

            猜你喜欢
            • 2018-02-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-06-25
            • 2018-09-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多