Являются ли эти две строки одинаковыми?...: 'vs'?? '?

Есть ли разница между этими двумя строками?

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

или

MyName = s.MyName ?? string.Empty

Ответ 1

UPDATE: я написал сообщение в блоге, которое более подробно обсуждает эту тему. http://www.codeducky.org/properties-fields-and-methods-oh-my/


Как правило, они возвращают тот же результат. Тем не менее, есть несколько случаев, когда вы ощутите заметные различия, когда MyName является свойством, поскольку геттер MyName будет выполняться дважды в первом примере и только один раз во втором примере.

Например, вы можете столкнуться с различиями в производительности от выполнения 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

и вот кусок с нулевым коалесцирующим оператором:

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.

Ответ 2

Если свойство больше, чем простой 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";
    }
}

Кроме того, в случае, отличном от строки, класс может перегрузить ==, чтобы сделать что-то другое, чем троичный оператор. Я не считаю, что тернарный оператор может быть перегружен.

Ответ 3

Единственное различие заключается в том, вы оцениваете s.MyName дважды или один раз. Первый будет делать это дважды в случае, если s.MyName не является нулевым, второй будет оценивать его только один раз.

В большинстве случаев это различие не имеет значения, и я бы пошел со вторым, потому что он более понятен и краток.

Ответ 4

Да, оба они одинаковы, и это оператор с нулевой связностью.

Он возвращает левый операнд, если операнд не равен нулю; в противном случае он возвращает правый операнд.

Если говорить об эффективности, то

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

Если я использую дизассемблер, тогда я вижу, что для первого оператора требуется 19 операторов, которые должны быть выполнены компилятором, тогда как второй оператор требовал выполнения только 12 операторов.

Ответ 5

Да, они делают то же самое. ?? является сокращением для проверки нулевого значения.

Ответ 6

Они выполняют ту же задачу.

Единственное отличие было бы читабельностью относительно того, понимают ли ваши коллеги или кто читает код, синтаксис.

EDIT: Кроме того, первый вариант может дважды оценить свойство MyName.

Ответ 7

Нет. Оба делают то же самое. Второй - эффективный. Возвращает фактическое значение, если оно не равно нулю. Иначе будет возвращено значение правой стороны.

Обратитесь к http://msdn.microsoft.com/en-us/library/ms173224.aspx

Надеюсь, что это поможет.