встроенные if и интерфейсы (полиморфизм) - PullRequest
1 голос
/ 20 января 2010
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();

Ответы [ 4 ]

7 голосов
/ 20 января 2010

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

? new Foo() : new Bar();

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

IFooBarable ting = a == 1 ? (IFooBarable)(new Foo()) : (IFooBarable)(new Bar());
5 голосов
/ 20 января 2010

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

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

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

4 голосов
/ 20 января 2010

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

Во-первых, мы рассуждаем "изнутри наружу". Когда вы говорите

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. Следовательно, мы не можем использовать тот факт, что это, скажем, идет к Фу в нашем анализе типа условного выражения! Это проблема курицы и яйца.

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

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

В вашем примере анализ выглядит так:

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

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

b ? new Cat() : new Dog()

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

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

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

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

1 голос
/ 20 января 2010

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

...