Тернарное выражение с интерфейсами в качестве базового класса - PullRequest
8 голосов
/ 21 марта 2012

Я пытаюсь создать троичное выражение и получаю следующую ошибку

"Тип условного выражения не может быть определен, поскольку не существует неявного преобразования между LiveSubscription и DisconnectedSubscription"

Та же логика работает в операторе if, но я хотел понять, почему она не будет работать в троичном выражении -

Вот суть того, что я пытаюсь сделать:

public interface IClientSubscription
{
    bool TryDisconnect();
}

public class LiveSubscription : IClientSubscription
{
    public bool TryDisconnect()
    {
        return true;
    }
}

public class DisconnectedSubscription : IClientSubscription
{
    public bool TryDisconnect()
    {
        return true;
    }
}

public class ConnectionManager
{
    public readonly IClientSubscription Subscription;

    public ConnectionManager(bool IsLive)
    {
        // This throws the exception
        Subscription = (IsLive)
            ? new LiveSubscription()
            : new DisconnectedSubscription();

        // This works
        if (IsLive)
        {
            Subscription = new LiveSubscription();
        }
        else
        {
            Subscription = new DisconnectedSubscription();
        }
    }
}

Я всегда мог переключить его на if / else, но сначала я хотел понять, что происходит не так!

1 Ответ

10 голосов
/ 21 марта 2012

Вам нужно привести хотя бы один из операндов к IClientSubscription:

Subscription = (IsLive)
            ? (IClientSubscription)new LiveSubscription()
            : new DisconnectedSubscription();

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


Пытаясь ответить на ваш вопрос в комментарии:

Нет, троичные выражения не являются чем-то вроде объекта, но троичное выражение является правой частью присваивания. Каждое выражение правой части присваивания имеет определенный тип, иначе было бы невозможно присвоить это выражение переменной слева.
Примеры:

  • var x = Guid.NewGuid()

    Правое выражение (Guid.NewGuid()) имеет тип Guid, потому что метод NewGuid() возвращает Guid.

  • var x = y.SomeMethod()

    Правое выражение имеет тип возвращаемого значения SomeMethod().

  • var x = IsLive ? "a" : 1

    Это явно неверно, не так ли? Какой тип должен быть x? A string или int?
    Это приведет к тому же сообщению об ошибке, которое вы получили с вашим кодом.

  • Ваш пример немного изменился:

    var subscription = (IsLive) ? new LiveSubscription()
                                : new DisconnectedSubscription();
    

    Обратите внимание на var перед subscription, теперь мы инициализируем новую переменную, а не существующую. Я думаю, что даже здесь очевидно, в чем проблема: какого типа должен быть subscription? LiveSubscription или DisconnectedSubscription? Это может быть ни то, ни другое, потому что в зависимости от IsLive это должен быть либо тот, либо другой.

О сравнении с if:

В вашем коде, где вы присваиваете новый LiveSubscription экземпляр или новый DisconnectedSubscription экземпляр Subscription, происходит неявное приведение к IClientSubscription, поскольку компилятор знает, что Subscription имеет тип IClientSubscription и LiveSubscription и DisconnectedSubscription могут быть неявно преобразованы в этот интерфейс.
Назначение с троичным выражением немного отличается, потому что компилятор сначала пытается вычислить троичное выражение, и только после этого он пытается присвоить его Subscription. Это означает, что компилятор не знает, что результат троичного выражения должен иметь тип IClientSubscription.

...