Условный оператор запутался, но почему?

Предположите два класса, оба потомка одного и того же суперкласса, например:

class MySuperClass{}
class A : MySuperClass{}
class B : MySuperClass{}

Тогда это назначение не будет передавать компилятор:

MySuperClass p = myCondition ? new A() : new B();

Компилятор жалуется, что A и B несовместимы (тип условного выражения не может быть определен, потому что нет никакого неявного преобразования между "A" и "B" [CS0173]). Но они оба типа MySuperClass, поэтому, на мой взгляд, это должно сработать. Не то чтобы это было большим делом; простое приведение - это все, что требуется для просвещения компилятора. Но, конечно же, это компромисс в компиляторе С#? Вы не согласны?

Ответы

Ответ 1

Результаты условного выражения должны быть одного типа. Это не так.

От MSDN, (?: Operator):

Любой тип выражения first_expression и second_expression должен быть одинаковым, или неявное преобразование должно существовать от одного типа к другому.

Так как A и B не являются одним и тем же типом, и вы, похоже, не определили неявное преобразование, компилятор жалуется.

Ответ 2

Просмотрите блог для некоторых интересных статей о том, почему компилятор С# делает/не делает то, что есть "очевидно" вам. В блоге написано Eric Lippert, один из разработчиков компилятора С#.

Ответ 3

Посмотрите раздел 7.14 спецификации языка

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

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

o Если неявное преобразование (§6.1) существует от X до Y, но не от Y до X, то Y является типом условное выражение.

o Если неявное преобразование (§6.1) существует от Y до X, но не от X до Y, то X является типом условное выражение.

o В противном случае тип выражения не может и время компиляции возникает ошибка.

. Если только один из х и у имеет типа, и как x, так и y, из могут быть легко конвертированы в типа, то это тип условное выражение.

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

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

Это почему вам нужно сделать явное приведение в вашем примере или в таких случаях, как nullables (int? foo = isBar ? 42 : (int?)null). Тип объявления не влияет на оценку, компилятор должен понять это из самого выражения.

Ответ 4

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

MySuperClass p = myCondition ? (MySuperClass)(new A()) : (MySuperClass)(new B());

Это означает, что условный оператор имеет обе стороны returnin gthe того же типа, который удовлетворяет компилятору.

Ответ 5

Условный оператор (как и любой другой оператор) должен определить тип, который представляет его выражение. В случае условного оператора он имеет двухэтапный процесс:

  • Являются ли операнды того же типа? Если это так, это тип выражения.
  • Существует ли неявное преобразование из одного из типов операндов в другое (но не в обоих направлениях)? Если это так, то "другое" является типом выражения.

Там нет поиска родословной, поскольку реализация может привести к скользкому наклону двусмысленности в том, что вы, как разработчик, можете указать в этом выражении. Должно ли все привести к object? Как насчет типов значений, которые затем будут неявно помещены в коробку? Как насчет интерфейсов? Если между двумя типами существует более одного общего интерфейса, какой из них следует выбрать?

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

Обратите внимание, что вам нужно всего лишь применить приведение к одному из операндов, а не к обоим.

Ответ 6

Роуленд Шоу подвел итоги довольно хорошо, но чтобы понять, почему общий предок не используется неявно, подумайте, что произойдет, если оба класса также будут реализовывать определенный интерфейс. Компилятор не смог бы определить, что использовать для типа условного оператора, и поэтому он будет вынужден использовать object вместо этого.