Синтаксис Ternary Operator для выбора реализации интерфейса

Мне интересно, почему эта строка кода не компилируется:

ILogStuff Logger = (_logMode) ? new LogToDisc() : new LogToConsole();

Обратите внимание, что оба класса LogToDisc и LogToConsole реализуют ILogStuff, а _logMode - булева переменная. Сообщение об ошибке, которое я получаю:

Ошибка 3: Тип условного выражения не может быть определен, потому что нет никакого неявного преобразования между 'xxx.LogToDisc' и 'xxx.LogToConsole'

Но почему это должно быть? Что мне не хватает?

Ответ 1

Для тернарного оператора нет неявного преобразования. Вам нужно передать возвращаемый объект тернарным оператором в ILogStuff. Это очень хорошо объясняет в ответе Эрика Липперта вопрос Неявная проблема преобразования в трехмерном состоянии

ILogStuff Logger = (_logMode) ? (ILogStuff) new LogToDisc() : (ILogStuff) new LogToConsole();

Из главы 7.13 Спецификации языка С#:

Второй и третий операнды оператора?: управляют типом условного выражения. Пусть X и Y - типы второго и третьего операндов. Тогда,

  • Если X и Y являются одним и тем же типом, то это тип условного выражения.
  • В противном случае, если неявное преобразование (§6.1) существует из X в Y, но не от Y до X, то Y является типом условного выражения.
  • В противном случае, если неявное преобразование (§6.1) существует от Y до X, но не от X до Y, то X является типом условного выражения.
  • В противном случае тип выражения не может быть определен, и возникает ошибка времени компиляции.

Ответ 2

Вам нужно отправить в интерфейс:

ILogStuff Logger = (_logMode) ? (ILogStuff)new LogToDisc() : new LogToConsole();

В спецификации описывается поведение условного оператора:

7.14 Условный оператор

Второй и третий операнды x и y оператора?: управляют тип условного выражения.

Если x имеет тип X, а y имеет тип Y тогда

  • Если из X в Y существует неявное преобразование (§6.1), но а не от Y до X, то Y - тип условного выражения.
  • Если неявное преобразование (§6.1) существует из Y в X, но не из X в Y, то X является типом условного выражения.
  • В противном случае тип выражения не может быть определен, и ошибка компиляции.

Нет никакого неявного преобразования между LogToDisc и LogToConsole в любом направлении, поэтому компиляция не выполняется. Если вы установите один из типов в ILogStuff, будет существовать неявное преобразование из другого типа.

Ответ 3

Сообщение верное, нет никакого неявного преобразования между этими двумя типами, они просто разделяют общий интерфейс. Но, разумеется, общий родительский подход не подразумевает возможности кастинга, так же как int не подразумевается скрытым в string, хотя оба имеют общий родительский элемент - Object.

Тернарный оператор ожидает, что тип результата обоих возможных значений будет одинаков - с точки зрения возможности сделать неявный бросок между ними. Поэтому вы должны сказать ему, что первое возвращаемое значение имеет тип ILogStuff:

ILogStuff Logger = (_logMode) ? (ILogStuff)new LogToDisc() : new LogToConsole();

Тогда второе возможное значение является правильным - существует неявное преобразование между LogToConsole типом и интерфейсом ILogStuff.

Ответ 4

Выражение должно возвращать общий тип обеих реализаций. Явным образом бросая экземпляры в интерфейс, выражение компилируется:

ILogStuff Logger = (_logMode) ? 
    (ILogStuff)new LogToDisc() : 
    (ILogStuff)new LogToConsole();

Ответ 5

Adil предоставил раздел, который определяет это поведение, но я хотел бы объяснить, почему это поведение разумно.

bool ? val1 : val2

Это выражение. Выражения должны иметь тип, который можно определить во время компиляции. Это ускоряет работу и быстрее ломает ошибки.

Теперь, если:

val является экземпляром MyObject1, который расширяет SomeParent и реализует MyInterface,
и val2 есть и экземпляр MyObject2, который расширяет SomeParent и реализует MyInterface

Как определить тип времени компиляции этого выражения? Мы могли бы попытаться найти общий тип между MyObject1 и MyObject2. Какое наиболее очевидное решение? Вы называете это SomeParent или MyInterface? и если бы у них было 2 интерфейса, которые вы бы выбрали?

Проблема в том, что это беспорядок, и потребуются некоторые довольно надуманные правила (и на самом деле есть еще несколько примеров, которые были бы менее ясными), что в конце дня было бы менее интуитивным, чем текущее определение.