【问题标题】:What do two question marks together mean in C#?在 C# 中,两个问号一起意味着什么?
【发布时间】:2010-11-07 00:53:05
【问题描述】:

跑过这行代码:

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

这两个问号是什么意思,是不是某种三元运算符? 在 Google 中很难查找。

【问题讨论】:

  • 这绝对是不是三元运算符——它只有两个操作数!这有点像条件运算符(三元),但空合并运算符是二元运算符。
  • 我在一次采访中解释了这一点,潜在雇主之前曾对我的 C# 能力表示怀疑,因为我之前一直在专业地使用 Java 一段时间。他们之前没有听说过,之后也没有质疑我对 C# 的熟悉程度 :)
  • @Jon Skeet 自从拒绝了披头士乐队的那个人以来,还没有发生过如此史诗般的失败。 :-) 从现在开始,只需将您的书的副本发送给他们,其中包含指向您 SO 个人资料的 url 链接写在封底上。
  • IainMH:不管怎样,我还没有完全开始写这本书。 (或者也许我只是在写第 1 章——类似的东西。)诚然,搜索我会很快找到我的博客和文章等。
  • 回复:q 中的最后一句 - 对于未来的参考,SymbolHound 非常适合这种事情,例如symbolhound.com/?q=%3F%3F&l=&e=&n=&u= [致任何可疑的人-我不以任何方式隶属,就像我找到一个好工具一样...]

标签: c# null-coalescing-operator


【解决方案1】:

它是空合并运算符,很像三元(立即如果)运算符。另见?? Operator - MSDN

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

扩展到:

FormsAuth = formsAuth != null ? formsAuth : new FormsAuthenticationWrapper();

进一步扩展为:

if(formsAuth != null)
    FormsAuth = formsAuth;
else
    FormsAuth = new FormsAuthenticationWrapper();

在英语中,它的意思是“如果左边的不是空的,就用那个,否则就用右边的。”

请注意,您可以按顺序使用任意数量的这些。以下语句会将第一个非空的Answer# 分配给Answer(如果所有答案都为空,则Answer 为空):

string Answer = Answer1 ?? Answer2 ?? Answer3 ?? Answer4;

另外值得一提的是,虽然上面的扩展在概念上是等价的,但每个表达式的结果只计算一次。例如,如果表达式是具有副作用的方法调用,这一点很重要。 (感谢@Joey 指出这一点。)

【讨论】:

  • 将这些链接起来可能很危险
  • @CodeBlend,这并不危险。如果你要扩展,你只会有一系列嵌套的 if/else 语句。语法很奇怪,因为你不习惯看到它。
  • 如果它也适用于空字符串,那将是天赐之物 :)
  • @Zyphrax,是的。类似于 || 在 JavaScript 中的工作方式。
  • @Gusdor ?? 是左关联的,所以a ?? b ?? c ?? d 等价于((a ?? b) ?? c ) ?? d。 “赋值运算符和三元运算符 (?:) 是右结合的。所有其他二元运算符都是左结合的。”来源:msdn.microsoft.com/en-us/library/ms173145.aspx
【解决方案2】:

?? 在值为空时为可空类型提供值。因此,如果 formsAuth 为空,它将返回新的 FormsAuthenticationWrapper()。

【讨论】:

    【解决方案3】:

    它是空合并运算符。

    http://msdn.microsoft.com/en-us/library/ms173224.aspx

    是的,除非你知道它叫什么,否则几乎不可能搜索到它! :-)

    编辑:这是另一个问题的一个很酷的功能。你可以把它们串起来。

    Hidden Features of C#?

    【讨论】:

    • 至少现在,搜索起来很容易,即使你不知道名字,我只是google了“双问号c#”
    【解决方案4】:

    它是三元运算符的简写。

    FormsAuth = (formsAuth != null) ? formsAuth : new FormsAuthenticationWrapper();
    

    或者对于那些不做三进制的人:

    if (formsAuth != null)
    {
      FormsAuth = formsAuth;
    }
    else
    {
      FormsAuth = new FormsAuthenticationWrapper();
    }
    

    【讨论】:

    • 我只更正了“三元”的拼写,但实际上您的意思是条件运算符。它恰好是 C# 中唯一的三元运算符,但在某些时候他们可能会添加另一个,此时“三元”将是模棱两可的,而“条件”则不会。
    • 它是您可以使用三元(条件)运算符的事情的简写。在您的长格式中,测试(!= null)和第二个formsAuth(在? 之后)都可以更改;在 null 合并形式中,两者都隐式采用您提供的值。
    【解决方案5】:

    合并运算符

    相当于

    FormsAuth = formsAUth == null ? new FormsAuthenticationWrapper() : formsAuth
    

    【讨论】:

      【解决方案6】:

      只是因为还没有人说出神奇的话:它是 null 合并运算符。它在C# 3.0 language specification 的第 7.12 节中定义。

      它非常方便,特别是因为它在一个表达式中多次使用时的工作方式。形式的表达:

      a ?? b ?? c ?? d
      

      如果表达式a不为空,将给出表达式的结果,否则尝试b,否则尝试c,否则尝试d。它在每一点短路。

      另外,如果d 的类型不可为空,则整个表达式的类型也不可为空。

      【讨论】:

      【解决方案7】:

      谢谢大家,这是我在 MSDN 网站上找到的最简洁的解释:

      // y = x, unless x is null, in which case y = -1.
      int y = x ?? -1;
      

      【讨论】:

      • 这暗示了 ??运算符——引入它是为了帮助处理可空类型。在您的示例中,“x”的类型为“int?” (可空)。
      • @vitule 不,如果空合并运算符的第二个操作数不可为空,则结果不可为空(而-1 只是一个普通的int,它是不可空的可以为空)。
      【解决方案8】:

      仅供娱乐(知道你们都是 C# 人 ;-)。

      我认为它起源于 Smalltalk,它已经存在多年了。它在那里定义为:

      在对象中:

      ? anArgument
          ^ self
      

      在 UndefinedObject(又名 nil 的类)中:

      ? anArgument
          ^ anArgument
      

      这有评估版 (?) 和非评估版 (??)。
      它经常出现在惰性初始化私有(实例)变量的 getter 方法中,在真正需要之前将其保留为 nil。

      【讨论】:

      • 听起来像是用 UserControl 上的属性包装 ViewState。如果之前未设置,则仅在第一次获取时初始化。 =)
      【解决方案9】:

      如果您熟悉 Ruby,它的 ||= 在我看来类似于 C# 的 ??。这是一些红宝石:

      irb(main):001:0> str1 = nil
      => nil
      irb(main):002:0> str1 ||= "new value"
      => "new value"
      irb(main):003:0> str2 = "old value"
      => "old value"
      irb(main):004:0> str2 ||= "another new value"
      => "old value"
      irb(main):005:0> str1
      => "new value"
      irb(main):006:0> str2
      => "old value"
      

      在 C# 中:

      string str1 = null;
      str1 = str1 ?? "new value";
      string str2 = "old value";
      str2 = str2 ?? "another new value";
      

      【讨论】:

      • x ||= y 脱糖成x = x || y 之类的东西,所以?? 实际上更类似于Ruby 中的普通||
      • 请注意?? 只关心null,而Ruby 中的|| 运算符与大多数语言一样,更多的是关于nullfalse 或任何可能的被认为是一个值为false 的布尔值(例如,在某些语言中,"")。这没有好坏之分,只是不同而已。
      【解决方案10】:

      这没什么危险。事实上,它是美丽的。 如果需要,您可以添加默认值,例如:

      代码

      int x = x1 ?? x2 ?? x3 ?? x4 ?? 0;
      

      【讨论】:

      • 所以 x1、x2、x3 和 x4 可以是 Nullable 类型,例如:int? x1 = null; 是吗
      • @KevinMeredith x1 - x4 必须是可以为空的类型:实际上说“结果是 0 如果 x4 是一个它不能的值是没有意义的可能采取”(null)。当然,这里的“可空类型”包括可空的 value 类型和引用类型。如果一个或多个链式变量(最后一个除外)不可为空,则为编译时错误。
      【解决方案11】:

      两个问号 (??) 表示它是一个 Coalescing 运算符。

      合并运算符从链中返回第一个 NON-NULL 值。你可以看到this youtube video,它实际演示了整个事情。

      但让我在视频中添加更多内容。

      如果您看到合并的英文意思,它表示“合并在一起”。例如下面是一个简单的合并代码,它链接了四个字符串。

      因此,如果str1null,它将尝试str2,如果str2null,它将尝试str3,依此类推,直到找到具有非空值的字符串。

      string final = str1 ?? str2 ?? str3 ?? str4;
      

      简单来说,合并运算符从链中返回第一个 NON-NULL 值。

      【讨论】:

      • 我喜欢这个解释,因为它有一个图表,然后最后一句解释的很简单!它使我易于理解并增强了我对使用的信心。谢谢!
      • 当我们只取coalesce 的英文意思时,我们可以预期结果是参数的某种联合或合并。对于它实际所做的事情,这当然不是最好的比喻。但是有一个长期存在的前身, t-sql COALESCE 函数,它做同样的事情,并且 - 很少有开发人员可能知道 - 有一个可变参数列表,因此它可以采用两个以上的候选值进行合并,只是像.NET ??运算符。
      【解决方案12】:

      这里的一些使用合​​并获取值的示例效率低。

      你真正想要的是:

      return _formsAuthWrapper = _formsAuthWrapper ?? new FormsAuthenticationWrapper();
      

      return _formsAuthWrapper ?? (_formsAuthWrapper = new FormsAuthenticationWrapper());
      

      这可以防止每次都重新创建对象。与私有变量保持为空并在每个请求上创建一个新对象不同,这样可以确保在创建新对象时分配私有变量。

      【讨论】:

      • 没有评估?? 快捷方式吗? new FormsAuthenticationWrapper(); 被评估当且仅当 _formsAuthWrapper 为 nil。
      • 是的,这就是重点。如果变量为空,您只想调用该方法。 @MSalters
      【解决方案13】:

      正如许多答案中正确指出的那样,“空合并运算符”(??),说到它,您可能还想查看它的表亲“空条件运算符”(?. 或 ?[) 是一个多次与 ??

      结合使用的运算符

      Null-conditional Operator

      用于在执行成员访问 (?.) 或索引 (?[) 操作之前测试 null。这些运算符可帮助您编写更少的代码来处理空值检查,尤其是在下降到数据结构时。

      例如:

      // if 'customers' or 'Order' property or 'Price' property  is null,
      // dollarAmount will be 0 
      // otherwise dollarAmount will be equal to 'customers.Order.Price'
      
      int dollarAmount = customers?.Order?.Price ?? 0; 
      

      没有 ?.?? 的旧方法是

      int dollarAmount = customers != null 
                         && customers.Order!=null
                         && customers.Order.Price!=null 
                          ? customers.Order.Price : 0; 
      

      这样比较冗长和繁琐。

      【讨论】:

        【解决方案14】:

        注意:

        我已经阅读了整个这个帖子和许多其他帖子,但我找不到像这样彻底的答案。

        由此我彻底明白了“为什么用??,什么时候用??,怎么用??。”

        来源:

        Craig McMurtry 开发的 Windows 通信基础 国际标准书号 0-672-32948-4

        可空值类型

        有两种常见的情况,人们想知道是否 已将值分配给值类型的实例。第一种是当实例表示数据库中的值时。在这种情况下,人们希望能够检查实例以确定数据库中是否确实存在值。与本书主题更相关的另一种情况是,实例表示从某个远程源接收的数据项。同样,人们想从实例中确定是否收到了该数据项的值。

        .NET Framework 2.0 合并了一个泛型类型定义,该定义提供了这样的情况,在这些情况下,人们希望将 null 分配给值类型的实例,并测试该实例的值是否为 null。该泛型类型定义是System.Nullable<T>,它将可以替代 T 的泛型类型参数限制为值类型。 从System.Nullable<T> 构造的类型的实例可以赋值为null;实际上,它们的值默认为空。因此,类型由 System.Nullable<T> 可以称为可空值类型。 System.Nullable<T> 有一个属性 Value,通过该属性将值分配给 如果实例的值不为空,则可以获取由它构造的类型。 因此,可以这样写:

        System.Nullable<int> myNullableInteger = null;
        myNullableInteger = 1;
        if (myNullableInteger != null)
        {
        Console.WriteLine(myNullableInteger.Value);
        }
        

        C# 编程语言提供了一种用于声明类型的缩写语法 从System.Nullable&lt;T&gt; 构造。该语法允许缩写:

        System.Nullable<int> myNullableInteger;
        

        int? myNullableInteger;
        

        编译器将阻止人们以这种方式尝试将可空值类型的值分配给普通值类型:

        int? myNullableInteger = null;
        int myInteger = myNullableInteger;
        

        它阻止了这样做,因为可空值类型可能具有 null 值,在这种情况下它实际上会具有该值,并且该值不能分配给普通值类型。虽然编译器会允许这段代码,

        int? myNullableInteger = null;
        int myInteger = myNullableInteger.Value;
        

        第二条语句将导致抛出异常,因为任何尝试 访问System.Nullable&lt;T&gt;.Value 属性是一个无效的操作,如果类型 从 System.Nullable&lt;T&gt; 构造的未分配有效的 T 值,在这种情况下没有发生。

        结论:

        将可空值类型的值分配给普通值类型的一种正确方法是使用System.Nullable&lt;T&gt;.HasValue 属性来确定是否已将有效值 T 分配给可空值类型:

        int? myNullableInteger = null;
        if (myNullableInteger.HasValue)
        {
        int myInteger = myNullableInteger.Value;
        }
        

        另一种选择是使用这种语法:

        int? myNullableInteger = null;
        int myInteger = myNullableInteger ?? -1;
        

        普通整数 myInteger 被分配了一个可空整数“myNullableInteger”的值,如果后者被分配了一个有效的整数值;否则,myInteger 的值为 -1。

        【讨论】:

          【解决方案15】:

          它是一个空值合并运算符,其工作方式类似于三元运算符。

              a ?? b  => a !=null ? a : b 
          

          另一个有趣的点是, “可以为空的类型可以包含一个值,也可以是未定义的”。 因此,如果您尝试将可空值类型分配给不可空值类型 你会得到一个编译时错误。

          int? x = null; // x is nullable value type
          int z = 0; // z is non-nullable value type
          z = x; // compile error will be there.
          

          所以要使用 ??运营商:

          z = x ?? 1; // with ?? operator there are no issues
          

          【讨论】:

            【解决方案16】:
            FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();
            

            等价于

            FormsAuth = formsAuth != null ? formsAuth : new FormsAuthenticationWrapper();
            

            但很酷的一点是,您可以像其他人所说的那样将它们锁起来。 没有涉及的一点是您实际上可以使用它来引发异常。

            A = A ?? B ?? throw new Exception("A and B are both NULL");
            

            【讨论】:

            • 您在帖子中包含示例真是太好了,尽管问题是在寻找操作员是什么或做什么的解释。
            【解决方案17】:

            ?? 运算符称为空合并运算符。如果操作数不为空,则返回左侧操作数;否则返回右手操作数。

            int? variable1 = null;
            int variable2  = variable1 ?? 100;
            

            如果variable1 不为空,则将variable2 设置为variable1 的值; 否则,如果variable1 == null,则将variable2 设置为100。

            【讨论】:

            • 这是我需要的。谢谢,伙计!
            【解决方案18】:

            其他人已经很好地描述了Null Coalescing Operator。在需要对 null 进行单次测试的情况下,缩短语法 ??= 可以增加可读性。

            旧版空测试:

            if (myvariable == null)
            {
                myvariable = new MyConstructor();
            }
            

            使用空合并运算符可以这样写:

            myvariable = myvariable ?? new MyConstructor();
            

            也可以用缩短的语法来写:

            myvariable ??= new MyConstructor();
            

            有些人觉得它更具可读性和简洁性。

            【讨论】:

            • 请记住,此功能仅在 C# 8 或更高版本中可用
            【解决方案19】:

            用最简单的方式,两个问号称为“合并运算符”,它从链中返回第一个非空值。

            例如,如果您从一个可为空的对象中获取一个值,在一个不可为空的变量中,那么您可以使用此运算符。 即

            int a = 1;
            诠释? b = null;
            a = b??0;

            上述等式的结果将为零,因为 b 为空并且我们使用了 ??运算符加上零,这意味着当且仅当 b 为 null 时它才会返回 0。

            int a = 1;
            诠释? b = 15;
            a = b??0;

            在上面的等式中,a 将得到值“15”,因为 b 具有有效值并且不为空。 还有,你不能用??不可为空对象上的运算符。

            在上面的例子中,我使用了 ?? 0,但是在 ?? 之后也可以使用一个完整的新方程。运算符。

            比如
            a = b ?? ( x==1 ? 10 : 15)
            我希望它能解决你的问题。

            【讨论】:

              猜你喜欢
              • 2023-01-10
              • 2022-11-16
              • 2012-05-29
              • 1970-01-01
              • 1970-01-01
              • 2017-03-02
              • 2013-06-05
              • 1970-01-01
              相关资源
              最近更新 更多