这两条线是一样的吗? …:'vs'??'?

这两条线是否有区别?

MyName = (s.MyName == null) ? string.Empty : s.MyName 

要么

 MyName = s.MyName ?? string.Empty 

更新:我写了一篇博客文章,更深入地讨论这个话题。 http://www.codeducky.org/properties-fields-and-methods-oh-my/


通常他们会返回相同的结果。 但是,有几种情况下,当MyName是一个属性时,您将体验到明显的差异,因为MyName getter将在第一个示例中执行两次,在第二个示例中只执行一次。

例如,您可能会遇到两次执行MyName性能差异:

 string MyName { get { Thread.Sleep(10000); return "HELLO"; } } 

或者,如果MyName是有状态的,则可能会从执行MyName两次获得不同的结果:

 private bool _MyNameHasBeenRead = false; string MyName { get { if(_MyNameHasBeenRead) throw new Exception("Can't read MyName twice"); _MyNameHasBeenRead = true; Thread.Sleep(10000); return "HELLO"; } } 

或者如果可以在不同的线程上更改MyName ,则可能会从执行MyName两次获得不同的结果:

 void ChangeMyNameAsync() { //MyName set to null in another thread which makes it //possible for the first example to return null Task.Run(() => this.MyName = null); } string MyName { get; set; } 

这是如何编译实际的代码。 首先是具有三元expression的作品:

 IL_0007: ldloc.0 // s IL_0008: callvirt s.get_MyName <-- first call IL_000D: brfalse.s IL_0017 IL_000F: ldloc.0 // s IL_0010: callvirt s.get_MyName <-- second call IL_0015: br.s IL_001C IL_0017: ldsfld System.String.Empty IL_001C: call set_MyName 

这里是一个空合并运算符:

 IL_0007: ldloc.0 // s IL_0008: callvirt s.get_MyName <-- only call IL_000D: dup IL_000E: brtrue.s IL_0016 IL_0010: pop IL_0011: ldsfld System.String.Empty IL_0016: call s.set_MyName 

正如你所看到的三元运算符的编译代码将进行两个调用来获取属性值,而空合并运算符将只能做1。

如果属性不仅仅是一个简单的getter,你可能会在第一个非空的情况下执行两次函数。

如果该属性处于有状态对象中,则对属性的第二次调用可能会返回不同的结果:

 class MyClass { private IEnumerator<string> _next = Next(); public MyClass() { this._next.MoveNext(); } public string MyName { get { var n = this._next.Current; this._next.MoveNext(); return n; } } public static IEnumerator<string> Next() { yield return "foo"; yield return "bar"; } } 

此外,在非string的情况下,类可能会重载==做一些不同于三元运算符的东西。 我不相信三元运算符可以超载。

唯一的区别是你是否评估s.MyName两次或一次。 第一个会在s.MyName不为null的情况下执行两次,第二个只会评估一次。

在大多数情况下,这个差别并不重要,我会和第二个一起去,因为它更简洁明了。

是的,两者都是相同的,它是空合并运算符 。

如果操作数不为null,则返回左边的操作数; 否则返回右手操作数。

如果我们谈论效率的话

 string MyName = (s.MyName == null) ? string.Empty : s.MyName; string MyName2 = s.MyName ?? string.Empty; 

如果我使用编译器,那么我可以看到第一个语句需要编译器执行的19个语句 ,而第二个语句只需要执行12个语句

是的,他们也这样做。 ?? 是检查null的简写。

他们完成相同的任务。

唯一的区别是可读性是关于你的同事还是正在阅读代码的人都理解语法。

编辑:另外第一个选项可以评估属性MyName两次。

不,两者都在做同样的事情。 第二个是有效的。 如果它不为null,则返回实际值。 否则,将返回右侧的值。

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

希望这可以帮助。