这两条线是否相同,'? ......:'vs'??'?

时间:2014-01-10 19:09:37

标签: c#

这两行之间有区别吗?

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

MyName = s.MyName ?? string.Empty

7 个答案:

答案 0 :(得分:166)

更新:我写了一篇博文,更深入地讨论了这个话题。 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; }  

以下是编译实际代码的方法。首先是三元表达式:

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

以下是null-coalescing运算符:

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

正如您所看到的,三元运算符的编译代码将进行两次调用以获取属性值,而null-coalescing运算符将只执行1。

答案 1 :(得分:26)

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

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

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

此外,在非字符串的情况下,类可能会重载==以执行与三元运算符不同的操作。我不相信三元运算符可能会过载。

答案 2 :(得分:9)

唯一的区别是您是评估s.MyName两次还是一次。第一个将在s.MyName不为空的情况下执行两次,第二个将只评估一次。

在大多数情况下,这种差异并不重要,我会选择第二种,因为它更清晰简洁。

答案 3 :(得分:5)

是的,两者都是一样的,而且是null-coalescing operator

  

如果操作数不为空,则返回左侧操作数;否则它会返回右手操作数。

如果我们谈论效率那么

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

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

他们完成了同样的任务。

只有不同之处在于您的同事或其他人是否正在阅读代码才能理解语法。

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

答案 6 :(得分:-1)

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

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

希望这有帮助。