#关于空合并运算符:在C中,两个问号在一起意味着什么?

What do two question marks together mean in C#?

穿过这行代码:

1
FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

这两个问号是什么意思,是某种三元运算符吗?在谷歌很难找到。


它是空合并运算符,非常类似于三元(立即if)运算符。看到了吗??操作员-MSDN。

1
FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

扩展到:

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

进一步扩展到:

1
2
3
4
if(formsAuth != null)
    FormsAuth = formsAuth;
else
    FormsAuth = new FormsAuthenticationWrapper();

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

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

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

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


只是因为还没有人说过这些神奇的词:它是空合并运算符。它在C 3.0语言规范的第7.12节中定义。

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

1
a ?? b ?? c ?? d

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

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


它是空合并运算符。

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

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

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

C的隐藏特征


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

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


当值为空时,??用于为可为空的类型提供值。因此,如果formsauth为空,它将返回新的formsauthenitationwrapper()。


enter image description here

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

联合运算符返回链中的第一个非空值。你可以看到这段YouTube视频,它实际上展示了整个事情。

但让我再加一点视频内容。

如果你看到合并的英文意思,它会说"合并在一起"。例如,下面是一个简单的合并代码,它链接了四个字符串。

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

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

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


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

1
2
3
4
5
6
7
8
9
10
11
12
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中:

1
2
3
4
string str1 = null;
str1 = str1 ??"new value";
string str2 ="old value";
str2 = str2 ??"another new value";


它是三元运算符的简称。

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

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

1
2
3
4
5
6
7
8
if (formsAuth != null)
{
  FormsAuth = formsAuth;
}
else
{
  FormsAuth = new FormsAuthenticationWrapper();
}


没什么危险的。实际上,它很漂亮。如果需要,可以添加默认值,例如:

代码

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


正如许多答案中正确指出的,"空合并运算符"(??),说到这一点,你可能还想看看它的表亲"空条件运算符"(?是吗?[)这是一个多次与它一起使用的运算符??

空条件运算符

Used to test for null before performing a member access (?.) or index (?[) operation. These operators help you write less code to handle null checks, especially for descending into data structures.

例如:

1
2
3
4
5
// 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;

没有旧方法吗?还有??这样做的目的是

1
2
3
4
int dollarAmount = customers != null
                   && customers.Order!=null
                   && customers.Order.Price!=null
                    ? customers.Order.Price : 0;

更冗长,更麻烦。


接合运算符

相当于

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


只为你的娱乐(知道你都是C人;-)。

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

宾语:

1
2
? anArgument
    ^ self

在UndefinedObject(aka nil's class)中:

1
2
? anArgument
    ^ anArgument

两者都有评估(?)和非评估版本(?)。它通常出现在用于惰性初始化的私有(实例)变量的getter方法中,直到真正需要时才保留为零。


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

你真正想要的是:

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

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

这将防止每次重新创建对象。与保留为空的私有变量和在每次请求时创建的新对象不同,这可以确保在创建新对象时分配私有变量。


注:

我已经阅读了这篇文章和其他许多文章,但我找不到像这样透彻的答案。

我完全理解"为什么要使用?"?何时使用??如何使用?"

来源:

Craig McMurtry发布的Windows通信基金会国际标准书号0-672-32948-4

可为空的值类型

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

.NET Framework 2.0包含一个通用类型定义,该定义提供了这样的情况,在这种情况下,需要将空值赋给值类型的实例,并测试实例的值是否为空。该泛型类型定义是System.Nullable,它约束可以将T替换为值类型的泛型类型参数。从System.Nullable构造的类型实例的值可以为空;实际上,默认情况下,它们的值为空。因此,类型构造自System.Nullable可以称为可为空的值类型。System.Nullable具有一个属性value,通过该属性将值分配给如果实例的值不为空,则可以获取从该实例构造的类型。因此,我们可以写:

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

C编程语言提供用于声明类型的缩写语法从System.Nullable构造。该语法允许缩写:

1
System.Nullable<int> myNullableInteger;

1
int? myNullableInteger;

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

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

它会阻止这样做,因为可以为空的值类型可以具有值空(在本例中是实际的值),并且该值不能分配给普通值类型。尽管编译器允许使用此代码,

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

第二个语句将导致引发异常,因为任何尝试如果类型为从System.Nullable构造,尚未为其分配有效的值T,但在本例中没有这样做。

结论:

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

1
2
3
4
5
int? myNullableInteger = null;
if (myNullableInteger.HasValue)
{
int myInteger = myNullableInteger.Value;
}

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

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

如果给普通整数myinteger分配了一个有效的整数值,则通过它为普通整数myinteger分配可以为空的整数"mynullableinteger"的值;否则,myinteger分配的值为-1。


1
FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

等于

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

但最酷的是,你可以像别人说的那样,把它们串起来。没有提到的一点是,您实际上可以使用它来抛出异常。

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


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

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

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


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

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

另一个有趣的观点是,"可以为空的类型可以包含值,也可以不定义。"因此,如果尝试将可空值类型赋给不可空值类型您将得到一个编译时错误。

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

那么用它来做?操作员:

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