C# 6.0中的问号和点运算符“?.”代表什么意思?

544

我们在VS2015预览版中拥有了新的运算符?.,可以像这样使用:

public class A {
   string PropertyOfA { get; set; }
}

...

var a = new A();
var foo = "bar";
if(a?.PropertyOfA != foo) {
   //somecode
}

它究竟是做什么的?


如果您有兴趣将可空布尔值 bool? 转换为 bool 值,请访问以下链接:https://dev59.com/gG025IYBdhLWcg3wVkUR - Mauricio Gracia Gutierrez
3个回答

753

这是 null条件 运算符。它的意思是:

"评估第一个操作数;如果为 null,则停止,并得到一个 null 结果。否则,将第二个操作数作为第一个操作数的成员进行评估。"

在您的示例中,重点是如果 anull,那么 a?.PropertyOfA 将会评估为 null 而不是抛出异常。然后它将使用字符串的 == 重载比较该 null 引用与 foo,发现它们不相等,执行将进入 if 语句体。

换句话说,就像这样:

string bar = (a == null ? null : a.PropertyOfA);
if (bar != foo)
{
    ...
}

……除了a只被评估一次之外。

要注意这可能会改变表达式的类型。例如,考虑FileInfo.Length 。那是一个long类型的属性,但如果你使用空值条件运算符,你最终得到的是一个long?类型的表达式:

FileInfo fi = ...; // fi could be null
long? length = fi?.Length; // If fi is null, length will be null

3
@SLaks:好的。在SyntaxKind中,显然是ConditionalAccessExpression,令人烦恼的是它既不是这个也不是那个…… - Jon Skeet
24
我更喜欢“Elvis”运算符这个名字 :P。 - Ahmed ilyas
3
记录一下,我见过这个运算符被称为五种不同的名称:安全导航、空值条件、空值传播、条件访问、Elvis。 - Gigi
1
如果你执行 string.Join(",", potentiallyNull ?? "") ,并且 potentiallyNull 为 null,那么 string.Join 返回一个空字符串。这不是你想要的吗? - Gabriel
4
值得注意的是,你也可以在数组上使用 null 条件运算符,例如:a?[x]。将其翻译为通俗易懂的语言并不改变原意。 - KevinVictor
显示剩余18条评论

138

当需要展平层次结构或映射对象时,这将非常有用。不要:

if (Model.Model2 == null
  || Model.Model2.Model3 == null
  || Model.Model2.Model3.Model4 == null
  || Model.Model2.Model3.Model4.Name == null)
{
  mapped.Name = "N/A"
}
else
{
  mapped.Name = Model.Model2.Model3.Model4.Name;
}

它可以像上面那样编写(与上面相同的逻辑)

mapped.Name = Model.Model2?.Model3?.Model4?.Name ?? "N/A";

DotNetFiddle.Net工作示例

(??或null合并运算符?或null条件运算符不同)。

它也可以在Action中的赋值运算符之外使用。而不是

Action<TValue> myAction = null;

if (myAction != null)
{
  myAction(TValue);
}

可以简化为:

myAction?.Invoke(TValue);

DotNetFiddle 示例

using System;

public class Program
{
  public static void Main()
  {
    Action<string> consoleWrite = null;
    
    consoleWrite?.Invoke("Test 1");
    
    consoleWrite = (s) => Console.WriteLine(s);
    
    consoleWrite?.Invoke("Test 2");
  }
}

结果:

测试2

基本上,我在 Model 后面应用了 ?. 运算符。我想知道它是否可以直接应用于模型,还是只能与导航属性一起使用?

? 或 null 条件运算符 对左值进行操作,不管其类型如何。编译器不关心右侧的值是什么。这是简单的编译器 魔法(意味着它做了你已经可以做的事情,只是以简化的方式)。

例如

  var a = model?.Value;

这句话的意思是“就相当于说”。
  var a = model == null ? null : model.Value;

在第二种情况下,检查空值的评估与返回的值无关。 空条件运算符 基本上只有在左侧的值为 null 时才始终返回 null。
成员的类型(方法、字段、属性、构造函数).Value 是不相关的。
你的 DotNetFiddle 示例无法工作的原因是使用的编译器 .Net 4.7.2 不兼容支持空条件运算符的 c# 版本。将其更改为 .Net 5 即可解决:

https://dotnetfiddle.net/7EWoO5


35
为了避免人们查找什么是 ??,这是一个空合并运算符,如果 Name 不为 null,则返回 Name,否则返回 "N/A"。 - Steve
6
@Erik Philips 我认为你需要添加 || Model.Model2.Model3.Model4.Name == null 来保持相同的逻辑,否则如果 Model.Model2.Model3.Model4.Namenullmapped.Name 将保持为 null - RazvanR
2
@ErikPhilips 我猜你可能没有理解我的意思。 请尝试想象一下,如果“Model.Model2.Model3.Model4.Name”为空,那么在你们两个的情况下会发生什么。 - RazvanR
7
这与第一个评论无关,因为它与您的第一个示例无关。在这种情况下,您将跳转到 else 分支,并且 mapped.Name = Model.Model2.Model3.Model4.Name -> mapped.Name = null,而您的第二个示例将替换为 mapped.Name = "N/A"。请参见编辑后的DotNetFiddle - derM
1
@Junaid 我已经更新了问题,请让我知道是否有什么不清楚的地方。 - Erik Philips
显示剩余3条评论

8
这对于C#来说相对较新,使我们在方法链接中调用函数时可以轻松处理空或非空值。
旧的实现方式是:
var functionCaller = this.member;
if (functionCaller!= null)
    functionCaller.someFunction(var someParam);

现在只需使用以下代码,就可以轻松完成:
member?.someFunction(var someParam);

我强烈推荐这个文档页面


快速问题..var user = db.Users.Where(m=>m.id == id).FirstOrDefault(); Console.WriteLine(user?.id); 这样行吗,还是 ?. 只能用于导航属性?请检查这个 fiddle: https://dotnetfiddle.net/2BblBv - Junaid

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