Как ведет себя правая ассоциативная связь с оператором нулевого слияния?

оператор слияния null является ассоциативным справа, что означает выражение вида

первый?? второй трети

оценивается как

первый?? (второй трети)

Исходя из вышеизложенного правила, я думаю, что следующий перевод не является правильным.

От:

Address contact = user.ContactAddress;
if (contact == null)
{
    contact = order.ShippingAddress;
    if (contact == null)
    {
        contact = user.BillingAddress;
    }
}

Для того, чтобы:

Address contact = user.ContactAddress ??
                  order.ShippingAddress ??
                  user.BillingAddress;

Вместо этого я думаю, что следующее правильно (пожалуйста, поправьте меня, если я ошибаюсь)

Address contact = (user.ContactAddress ?? order.ShippingAddress) ??
                   user.BillingAddress;

4 ответа

Решение

Спекуляция на самом деле противоречива.

Раздел 7.13 спецификации C# 4 гласит:

Нулевой объединяющий оператор является ассоциативным справа, что означает, что операции сгруппированы справа налево. Например, выражение вида a ?? b ?? c оценивается как a ?? (b ?? c),

С другой стороны, как было указано, 7.3.1 утверждает, что:

За исключением операторов присваивания, все бинарные операторы являются левоассоциативными

Я полностью согласен, что для простых случаев не имеет значения, как вы делаете группировку... но могут быть случаи, когда это действительно имеет значение из-за неявных преобразований типов, делающих интересные вещи, если операнды имеют разные типы.

Я рассмотрю это далее, пингую Мэдса и Эрика, и добавлю опечатку для соответствующего раздела C# в Depth (который вдохновил этот вопрос).

РЕДАКТИРОВАТЬ: Хорошо, теперь у меня есть пример, где это имеет значение... и оператор объединения нулей определенно прямо- ассоциативный, по крайней мере в компиляторе MS C# 4. Код:

using System;

public struct Foo
{
    public static implicit operator Bar(Foo input)
    {
        Console.WriteLine("Foo to Bar");
        return new Bar();
    }

    public static implicit operator Baz(Foo input)
    {
        Console.WriteLine("Foo to Baz");
        return new Baz();
    }
}

public struct Bar
{
    public static implicit operator Baz(Bar input)
    {
        Console.WriteLine("Bar to Baz");
        return new Baz();
    }
}

public struct Baz
{
}


class Test
{
    static void Main()
    {
        Foo? x = new Foo();
        Bar? y = new Bar();
        Baz? z = new Baz();

        Console.WriteLine("Unbracketed:");
        Baz? a = x ?? y ?? z;
        Console.WriteLine("Grouped to the left:");
        Baz? b = (x ?? y) ?? z;
        Console.WriteLine("Grouped to the right:");
        Baz? c = x ?? (y ?? z);
    }
}

Выход:

Unbracketed:
Foo to Baz
Grouped to the left:
Foo to Bar
Foo to Bar
Bar to Baz
Grouped to the right:
Foo to Baz

Другими словами,

x ?? y ?? z

ведет себя так же, как

x ?? (y ?? z)

но не такой как

(x ?? y) ?? z

В настоящее время я не уверен, почему есть два преобразования из Foo в Бар при использовании (x ?? y) ?? z - Мне нужно проверить это более тщательно...

РЕДАКТИРОВАТЬ: у меня теперь есть еще один вопрос, чтобы охватить двойное преобразование...

Ответ Джона правильный.

Просто чтобы быть ясно: ?? оператор в C# является ассоциативным справа. Я только что прошел через парсер двоичного оператора и проверил, что парсер обрабатывает ?? как право-ассоциативный.

Как указывает Джон, в спецификации говорится, что ?? оператор является ассоциативным справа, и что все бинарные операторы, кроме присваивания, являются ассоциативными слева. Поскольку спецификация противоречит самой себе, очевидно, что только один из них может быть правильным. Я буду изменять спецификации, чтобы сказать что-то вроде:

За исключением операторов простого присваивания, составного присваивания и объединения нулей, все бинарные операторы являются левоассоциативными

Я не понимаю, как это важно, оба:

(a ?? b) ?? c

а также

a ?? (b ?? c)

иметь тот же результат!

Оба работают как ожидалось и фактически идентичны, потому что выражения включают простые типы (спасибо @Jon Skeet). Это будет цепочка к первому ненулевому слева направо в ваших примерах.

Приоритет (спасибо @Ben Voigt) более интересен при объединении этого оператора с операторами другого приоритета:

value = A ?? B ? C : D ?? E;

По сути, ассоциативность по своей сути выражается через приоритет оператора или введенные пользователем подвыражения (круглые скобки).

Другие вопросы по тегам