跑过这行代码:
FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();
两个问号是什么意思,是不是某种三元运算符?在 Google 中很难查找。
它是空合并运算符,非常类似于三元(立即如果)运算符。另见?? 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 指出这一点。)
只是因为还没有人说出神奇的话:它是 null 合并运算符。它在 C# 3.0 language specification 的第 7.12 节中定义。
它非常方便,特别是因为它在表达式中多次使用时的工作方式。形式的表达:
a ?? b ?? c ?? d
如果表达式 a
不为空,将给出表达式的结果,否则尝试 b
,否则尝试 c
,否则尝试 d
。它在每一点短路。
此外,如果 d
的类型不可为空,则整个表达式的类型也不可为空。
它是空合并运算符。
http://msdn.microsoft.com/en-us/library/ms173224.aspx
是的,除非你知道它叫什么,否则几乎不可能搜索到! :-)
编辑:这是另一个问题的一个很酷的功能。你可以把它们串起来。
谢谢大家,这是我在 MSDN 网站上找到的最简洁的解释:
// y = x, unless x is null, in which case y = -1.
int y = x ?? -1;
-1
只是一个普通的 int
,它不可为空)。
https://i.stack.imgur.com/LHdqP.png
两个问号 (??) 表示它是一个 Coalescing 运算符。
合并运算符从链中返回第一个 NON-NULL 值。您可以看到 this youtube video,它实际演示了整个事情。
但是,让我在视频中添加更多内容。
如果您看到合并的英文意思,它表示“合并在一起”。例如下面是一个链接四个字符串的简单合并代码。
因此,如果 str1
是 null
,它将尝试 str2
,如果 str2
是 null
,它将尝试 str3
,依此类推,直到找到具有非空值的字符串。
string final = str1 ?? str2 ?? str3 ?? str4;
简单来说,Coalescing 运算符从链中返回第一个 NON-NULL 值。
COALESCE
函数,它做同样的事情,并且 - 很少有开发人员可能知道 - 有一个可变参数列表,因此它可以采用两个以上的候选值进行合并,只是像.NET ??操作员。
??
用于在值为 null 时为可空类型提供值。因此,如果 formsAuth 为空,它将返回新的 FormsAuthenticationWrapper()。
这是三元运算符的简写。
FormsAuth = (formsAuth != null) ? formsAuth : new FormsAuthenticationWrapper();
或者对于那些不做三元的人:
if (formsAuth != null)
{
FormsAuth = formsAuth;
}
else
{
FormsAuth = new FormsAuthenticationWrapper();
}
!= null
) 和第二个 formsAuth
(在 ?
之后)都可以更改;在空合并形式中,两者都隐含地采用您提供的值。
如果您熟悉 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 中的 ||
运算符与大多数语言一样,更多地涉及 null
、false
或任何可被视为布尔值的 { 5}(例如在某些语言中,""
)。这没有好坏之分,只是不同而已。
合并算子
相当于
FormsAuth = formsAUth == null ? new FormsAuthenticationWrapper() : formsAuth
这没有什么危险的。事实上,它是美丽的。如果需要,您可以添加默认值,例如:
代码
int x = x1 ?? x2 ?? x3 ?? x4 ?? 0;
int? x1 = null;
对吗
x1
- x4
必须是可为空的类型:有效地说“如果 x4
是一个它不可能采用的值,则结果是 0
”(null
)是没有意义的.当然,这里的“可空类型”包括可空的 value 类型和引用类型。如果一个或多个链式变量(最后一个除外)不可为空,则为编译时错误。
正如许多答案中正确指出的那样,“空合并运算符”(??),说到它,您可能还想查看它的表亲“空条件运算符”(?. 或 ?[),它是一个运算符很多时候它与??
用于在执行成员访问 (?.) 或索引 (?[) 操作之前测试 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;
这更加冗长和繁琐。
笔记:
我已经阅读了整个这个线程和许多其他线程,但我找不到像这样彻底的答案。
通过它我完全理解了“为什么使用??以及何时使用??以及如何使用??”。
资源:
Craig McMurtry 发起的 Windows 通信基础 ISBN 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,如果实例的值不为 null,则可以通过该属性获得分配给从它构造的类型的实例的值。因此,可以写:
System.Nullable<int> myNullableInteger = null;
myNullableInteger = 1;
if (myNullableInteger != null)
{
Console.WriteLine(myNullableInteger.Value);
}
C# 编程语言提供了一种用于声明从 System.Nullable<T>
构造的类型的缩写语法。该语法允许缩写:
System.Nullable<int> myNullableInteger;
至
int? myNullableInteger;
编译器将阻止人们尝试以这种方式将可空值类型的值分配给普通值类型:
int? myNullableInteger = null;
int myInteger = myNullableInteger;
它阻止了这样做,因为可空值类型可能具有 null 值,在这种情况下它实际上会具有该值,并且该值不能分配给普通值类型。虽然编译器会允许这段代码,
int? myNullableInteger = null;
int myInteger = myNullableInteger.Value;
第二条语句将导致抛出异常,因为如果从 System.Nullable<T>
构造的类型没有被分配有效的 T 值,则任何访问 System.Nullable<T>
.Value 属性的尝试都是无效操作,这在本例中没有发生案子。
结论:
将可空值类型的值分配给普通值类型的一种正确方法是使用 System.Nullable<T>
.HasValue 属性来确定 T 的有效值是否已分配给可空值类型:
int? myNullableInteger = null;
if (myNullableInteger.HasValue)
{
int myInteger = myNullableInteger.Value;
}
另一种选择是使用以下语法:
int? myNullableInteger = null;
int myInteger = myNullableInteger ?? -1;
通过它为普通整数 myInteger 分配可空整数“myNullableInteger”的值,如果后者已被分配一个有效的整数值;否则,myInteger 被赋值为 -1。
仅供娱乐(知道你们都是 C# 人 ;-)。
我认为它起源于 Smalltalk,它已经存在很多年了。它在那里定义为:
在对象中:
? anArgument
^ self
在 UndefinedObject(又名 nil 的类)中:
? anArgument
^ anArgument
有评估(?)和非评估版本(??)。它经常出现在惰性初始化私有(实例)变量的 getter 方法中,在真正需要之前将其保留为 nil。
此处使用合并获取值的一些示例效率低下。
你真正想要的是:
return _formsAuthWrapper = _formsAuthWrapper ?? new FormsAuthenticationWrapper();
或者
return _formsAuthWrapper ?? (_formsAuthWrapper = new FormsAuthenticationWrapper());
这可以防止每次都重新创建对象。与私有变量保持为空并在每个请求上创建一个新对象不同,这确保了在创建新对象时分配私有变量。
??
捷径吗? 当且仅当 _formsAuthWrapper
为 nil 时才会评估 new FormsAuthenticationWrapper();
。
其他人已经很好地描述了Null Coalescing Operator
。在需要对 null 进行单个测试的情况下,缩短的语法 ??=
可以增加可读性。
旧版空测试:
if (myvariable == null)
{
myvariable = new MyConstructor();
}
使用空合并运算符可以写成:
myvariable = myvariable ?? new MyConstructor();
也可以用缩短的语法编写:
myvariable ??= new MyConstructor();
有些人觉得它更易读、更简洁。
以最简单的方式,两个问号称为“合并运算符”,它从链中返回第一个非空值。
例如,如果您从一个可以为空的对象中获取一个不可为空的变量中的值,那么您可以使用此运算符。 IE
整数a = 1;诠释? b = 空; a = b??0;
上述等式的结果将为零,因为 b 为空并且我们使用了 ??运算符与零一起,这意味着当且仅当 b 为空时它将返回 0。
整数a = 1;诠释? b = 15; a = b??0;
在上面的等式中,a 将得到值“15”,因为 b 具有有效值并且不为空。还有,你不能用??非可空对象上的运算符。
在上面的例子中,我使用了 ?? 0,但是在 ?? 之后也可以使用一个完整的新方程。操作员。
比如 a = b ?? ( x==1 ? 10 : 15) 我希望它能解决你的问题。
??
运算符称为空合并运算符。如果操作数不为空,则返回左侧操作数;否则返回右手操作数。
int? variable1 = null;
int variable2 = variable1 ?? 100;
如果 variable1
不为空,则将 variable2
设置为 variable1
的值;否则,如果是 variable1 == null
,则将 variable2
设置为 100。
它是一个空值合并运算符,其工作方式类似于三元运算符。
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
FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();
相当于
FormsAuth = formsAuth != null ? formsAuth : new FormsAuthenticationWrapper();
但它很酷的一点是你可以像其他人所说的那样把它们锁起来。没有提到的一点是您实际上可以使用它来引发异常。
A = A ?? B ?? throw new Exception("A and B are both NULL");
||
在 JavaScript 中的工作方式。??
是左关联的,所以a ?? b ?? c ?? d
等价于((a ?? b) ?? c ) ?? d
。 “赋值运算符和三元运算符 (?:) 是右结合的。所有其他二元运算符都是左结合的。”来源:msdn.microsoft.com/en-us/library/ms173145.aspx