Вопрос

public class Foo : IFooBarable {...}
public class Bar : IFooBarable {...}

Так почему же тогда это не скомпилируется?..

int a = 1;
IFooBarable ting = a == 1 ? new Foo() : new Bar();

но это произойдет...

IFooBarable ting = a == 1 ? new Foo() : new Foo();
IFooBarable ting = a == 1 ? new Bar() : new Bar();
Это было полезно?

Решение

Компилятор сначала пытается вычислить правостороннее выражение:

? new Foo() : new Bar();

Между этими двумя нет неявного преобразования, отсюда и сообщение об ошибке.Ты можешь это сделать:

IFooBarable ting = a == 1 ? (IFooBarable)(new Foo()) : (IFooBarable)(new Bar());

Другие советы

Это описано в разделе 7.13 спецификации языка C #.По сути, что убивает этот сценарий, так это то, что должно быть неявное преобразование между типами двух значений для троичных операндов.Это преобразование учитывается при отсутствии типа переменной.

Так что либо Foo должен быть конвертируемым в Bar или наоборот.Ни то, ни другое не приводит к возникновению ошибки компиляции.

Последние 2 работают, хотя бы потому, что они рассматривают только 1 тип (либо Foo или Bar).Поскольку они одного типа, определить тип выражения несложно, и оно отлично работает.

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

Первый заключается в том, что мы рассуждаем "изнутри наружу".Когда ты говоришь

double x = 2 + y;

сначала мы определяем тип x, затем тип 2, затем тип y, затем тип (2 + y) и, наконец, мы выясняем, имеют ли x и (2+ y) совместимые типы.Но мы НЕ используем тип x при принятии решения о том, что такое тип 2, y или 2+ y.

Причина, по которой это хорошее правило, заключается в том, что часто тип "получателя" - это именно то, что мы пытаемся определить:

void M(Foo f) {}
void M(Bar b) {}
...
M(x ? y : z);

Что нам здесь делать?Мы должны определить тип условного выражения, чтобы выполнить разрешение перегрузки, чтобы определить, относится ли это к Foo или Bar .Следовательно, мы не можем использование тот факт, что это, скажем, будет Foo в нашем анализе типа условного выражения!Это проблема курицы и яйца.

Исключением из этого правила являются лямбда-выражения, которые делай возьмите их тип из их контекста.Заставить эту функцию работать должным образом было безумно сложно;видишь мой серия блогов о лямбда-выражениях против анонимных методов, если вам интересно.

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

В вашем примере анализ выглядит следующим образом:

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

В соответствии с первым пунктом, мы не рассуждаем снаружи-внутрь;мы не используем тот факт, что нам известен тип переменной, к которой мы переходим, для того, чтобы определить тип выражения.Но самое интересное сейчас заключается в том, что когда у вас есть

b ? new Cat() : new Dog()

мы говорим "тип условного выражения является лучшим типом в наборе {Cat, Dog}".Мы НЕ говорим "тип условного выражения является наилучшим типом, совместимым как с Кошкой, так и с Собакой".Это было бы глупо, но мы этого не делаем.Вместо этого мы говорим: "результатом должно быть то, что мы действительно видели", и из этих двух вариантов ни один не является явным победителем.Если бы вы сказали

b ? (Animal) (new Cat()) : new Dog()

тогда у нас есть выбор между Животным и Собакой, и Животное - явный победитель.

Теперь обратите внимание, что на самом деле мы неправильно реализуем спецификацию C # при выполнении этого анализа типов!Для получения подробной информации об ошибке смотрите мой Статья на нем.

Потому что тип условного выражения всегда выводится из двух его частей, а не из переменной, к которой должен быть применен результат.Этот вывод работает только тогда, когда типы равны или один из них совместим по ссылке с другим.В этом случае ни один из двух типов не является ссылочно совместимым с другим.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top