Несколько вещей.
Прежде всего, условный оператор является ternary operator, not a tertiary operator.
Во-вторых, я отмечаю, что в ваших образцах кода два образца кода, которые предназначены, чтобы быть эквивалентными не являются:
short foo;
if (isValid)
foo = -1;
else
getFoo();
не то же самое, как
short foo = isValid ? (short)-1 : getFoo();
Прежние листья Foo Unassigned если isValid - false. Последний присваивает foo независимо от значения isValid.
Я предполагаю, что вы имели в виду
short foo;
if (isValid)
foo = -1;
else
foo = getFoo();
и что, кроме того, getFoo() возвращает коротка.
Вопрос в том, почему преобразование в условном операторе без тикового типа является незаконным, но в результате утверждения if является законным.
Это законно в если заявление, поскольку раздел 6.1.9 из спецификации состояний:
Константа-выражение типа Int может быть преобразовано в тип SByte, байт, короткий, UShort, UINT или ulong, если значение константного выражения находится в пределах диапазона типа назначения.
-1 - постоянное выражение типа int, которое находится в диапазоне коротких, поэтому оно может быть преобразовано в короткое неявное.
Итак, почему условное выражение формирует фикцию?
Первое, что мы должны четко определить, является правилом, что тип условного выражения определяется из его содержания, а не от его контекста. Тип выражения в правой части присваивания не зависит от того, к чему он назначен! Предположим, что у вас
short M(short x){...}
int M(int x){...}
short y = M(-1);
Я не думаю, что вы ожидали бы разрешение перегрузки сказать «хорошо, я обычно выбрать M (INT), так как -1 является INT, но нет, я заеду M (короткое), потому что иначе назначение не будет работать ». Разрешение перегрузки ничего не знает о , где результат. Задача состоит в том, чтобы выработать правильную перегрузку на основе приведенных аргументов, а не на основе контекста вызова.
Определение типа условного выражения работает одинаково. Мы не смотрим на тип, к которому он идет, мы смотрим на типы, которые находятся в выражении.
ОК, поэтому мы установили, что тот факт, что это назначается короткому, не имеет значения для определения типа выражения. Но это все еще оставляет вопрос «Почему тип условного выражения int, а не короткий?»
Это очень хороший вопрос. Пойдем к спецификации.
Второй и третий операнды x и y оператора?: Управляют типом условного выражения.
Если имеет тип X и Y имеет тип Y, то:
Если существует неявное преобразование из X в Y, но не от Y к X, то Y является тип условного выражения.
Если неявное преобразование существует от Y до X, но не от X до Y, то X является типом условного выражения.
В противном случае тип выражения не может быть определен, и возникает ошибка времени компиляции.
В этом случае оба операнда имеют тип. (В формулировке о «если x есть тип ...» для случая, когда у вас есть нуль или лямбда, у них нет типов!) Первый операнд имеет тип int, второй - тип короткий.
Неявное преобразование существует от short до int, но не от int до short. Поэтому тип условного выражения - int, который не может быть назначен коротким.
Теперь можно сказать, что этот алгоритм не так хорош, как мог бы быть. Мы могли бы значительно усложнить алгоритм для рассмотрения всех случаев, когда существовали два возможных типа «кандидата» - в этом случае int и short являются правдоподобными кандидатами, поскольку обе ветви могут быть конвертированы как в int, так и в short , если рассматривать их как конкретные выражения , а не просто с типами. В этом случае мы могли бы сказать, что предпочтительным типом был меньший двух типов.
(Иногда в C# мы говорим, что более общий тип двух типов является лучшим типом, но в этом случае вы хотели бы, чтобы мы выбрали более конкретный. Язык несовместим в этом конкретном аспекте дизайна, к сожалению , Я лично предпочел бы, чтобы мы всегда выбирали более конкретные, но существуют сценарии вывода типов, в которых это было бы нарушением изменений сейчас.)
Я рассмотрел это еще в 2006 году. При разработке поведения того, как LINQ имеет дело с ситуациями где есть несколько типов на выбор, и один должен быть выбран как «лучший», мы заметили, что условному оператору уже пришлось решить эту проблему, и что, кроме того, в C# 2 он фактически не реализован в соответствии с к спецификации. Об этом шла долгая дискуссия, и мы в конечном итоге внесли некоторые незначительные изменения в спецификацию для условного оператора, чтобы привести ее в соответствие с ее реализованным (и желательным) поведением. Однако мы решили не предпринимать больших изменений в изменении настройки алгоритма, чтобы использовать меньший из двух возможных типов, когда их было несколько.
Для некоторых размышлений по этой проблеме см мои посты с 2006 года на нем:
Вы могли бы выбрать лучшие имена переменных и функций ... – Oded
Каков тип возврата 'getFoo'? – Oded
@Oded getFoo имеет тип короткий ..Это не проблема – jsmith