在C#中,两个问号连在一起代表什么意思?

1997

我遇到了这行代码:

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

这两个问号代表什么意思?是一种三元运算符吗?在谷歌上查找很困难。


70
这绝对不是一个三元运算符——它只有两个操作数!它有点像条件运算符(是三元的),但空值合并运算符是一个二元运算符。 - Jon Skeet
9
关于问题中的最后一句话,供以后参考,SymbolHound非常适合这种情况,例如:http://www.symbolhound.com/?q=%3F%3F&l=&e=&n=&u=。对于任何怀疑的人,请注意我与该工具没有任何关系,我只是在发现好工具时喜欢使用它。 - Steve Chambers
7
搜索 C# ?? | C# '??' | C# "??" 并没有得到预期的结果。这个搜索引擎是否在测试 C# 是否为空,并表示,不是 - 它实际上是 C# - 这里是您的 C# 结果 - 哎呀! - Piotr Kula
6
@ppumkin 只需在谷歌中搜索 double question mark c# - user692942
19个回答

2628

这是空值合并运算符,类似于三元(立即- 如果)运算符。 另请参见?? 运算符 - MSDN

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

扩展为:

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

进一步扩展为:

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

这段话的意思是,“如果左边的内容不为空,则使用左边的内容,否则使用右边的内容。”

请注意,您可以使用任意数量的这些语句。以下语句将为 Answer 赋值第一个非空的 Answer#(如果所有的 Answer 都为空,则 Answer 也将为空):

string Answer = Answer1 ?? Answer2 ?? Answer3 ?? Answer4;
同样值得一提的是,尽管上述展开在概念上是等价的,但每个表达式的结果只被计算一次。如果例如一个表达式是具有副作用的方法调用,这一点就很重要。(感谢 @Joey 指出这一点。)

26
这些可能会有潜在危险,最好不要把它们链在一起。 - Paul C
95
@CodeBlend,这并不危险。如果你要扩展,你只需要一系列嵌套的if/else语句。语法之所以奇怪,是因为你不习惯看到它。 - joelmdev
6
如果第一个参数第一次返回非空值,它会被评估两次吗?例如:x = f1() ?? f2(); 当 'f1' 第一次返回非空时,会被评估两次吗? - Alex
19
@Gusdor ?? 是左结合的,所以 a ?? b ?? c ?? d 等价于 ((a ?? b) ?? c) ?? d。 "赋值运算符和三元运算符(?:)是右结合的。所有其他二元运算符都是左结合的。" 来源:http://msdn.microsoft.com/en-us/library/ms173145.aspx - Mark E. Haase
7
请注意,这里展开的方式并不完全正确,因为语言保证左操作数只被计算一次。在这种特定情况下这并不是问题,但是当左边有一个比局部变量更复杂的表达式时,这点就非常重要了。 - Joey
显示剩余7条评论

332

正是因为没有人说出魔法词语,所以我告诉你吧:这就是空值合并运算符。它在C# 3.0语言规范的第7.12节中定义。

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

a ?? b ?? c ?? d

如果表达式a不是空的,它将返回结果a。否则它会尝试b,然后尝试c,最后尝试d。在每个点上都进行短路处理。

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


81

39

谢谢大家,以下是我在MSDN网站上找到的最简明扼要的解释:

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

4
这表明了“??”运算符的一个重要方面——它被引入来帮助处理可空类型。在你的例子中,“x”是“int?”(Nullable<int>)类型。 - Drew Noakes
9
不,如果空值合并运算符的第二个操作数是不可为空的,则结果也是不可为空的(而“-1”只是普通的“int”,它是不可为空的)。 - Suzanne Soy

33

图片描述

两个问号(??)表示这是一个合并运算符。

合并运算符从链中返回第一个非空值。您可以观看这个YouTube视频来实际演示整个过程。

但让我为视频所说的内容增加更多说明。

如果您查看合并的英文含义,它的意思是“巩固在一起”。例如下面是一个简单的合并代码,用于链接四个字符串。

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

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

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


25

?? 是用于在可为空类型的值为null时提供一个默认值。 因此,如果 formsAuth 为空,它将返回新的 FormsAuthenticationWrapper()。


19

这是三元运算符的缩写。

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

对于不使用条件运算符的人,可以这样写:

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

3
我只纠正了 "ternary" 的拼写,但你真正指的运算符是条件运算符。它恰好是 C# 中唯一的三元运算符,但在某些时候他们可能会添加另一个三元运算符,在那时,“ternary” 将变得模棱两可,但“conditional” 不会改变其意思。 - Jon Skeet
1
这是使用三元(条件)运算符可以执行的某些东西的速记。在您的长格式中,测试(!= null)和第二个formsAuth(在之后)都可以更改;在null合并形式中,两者都隐含地采用了您提供的值。 - Bob Sammers

18

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

传统的null测试:

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

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

myvariable = myvariable ?? new MyConstructor();

也可以用缩写语法来写:

myvariable ??= new MyConstructor();
一些人发现它更易读且简洁。

一些人发现它更易读且简洁。


1
请注意,此功能仅适用于C# 8或更高版本。 - cfaz

13

如果你熟悉 Ruby,它的 ||= 看起来对我来说就像是 C# 的 ??。以下是一些 Ruby 代码:

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";

5
x ||= y 被展开后变成 x = x || y,因此在 Ruby 中 ?? 实际上更类似于普通的 || - qerub
8
请注意,?? 只关心 null,而在 Ruby 中,类似于大多数编程语言的 || 运算符更关注 nullfalse 或任何被视为布尔值为 false 的内容(例如在某些语言中是 "")。这不是好事或坏事,仅仅是一种差异。 - Tim S.

13

众多回答中正确指出了"null合并运算符" (??),此外你可能还想查看其近亲 "Null-conditional Operator" (?.?[)。这是一种操作符,经常与??结合使用。

Null-conditional Operator

用于在执行成员访问(?.)或索引(?[)操作之前测试是否为空。这些操作符可以帮助你写更少的代码来处理空检查,特别是对于深入数据结构时。

例如:

// 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; 

更加冗长而且繁琐。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接