Почему мне нужно приводить тип int в тернарном выражении?

Возможный дубликат:
Условный оператор не может привести к неявному преобразованию?

Я попал в странную ситуацию и хочу знать, почему я должен это делать. Я использую .NET 3.5.

Это работает:

short foo;

if (isValid)
    foo = -1;
else
    foo = getFoo();

Это не работает:

short foo;
foo = isValid ? -1 : getFoo();

Мне нужно привести -1:

short foo;
foo = isValid ? (short)-1 : getFoo();

Что по-другому делает тернарное выражение? Он считает, что -1 - это int, который нужно преобразовать в шорт. Но почему?


person jsmith    schedule 30.06.2010    source источник
comment
Вы могли бы выбрать более подходящие имена переменных и функций ...   -  person Oded    schedule 30.06.2010
comment
Какой тип возврата getFoo?   -  person Oded    schedule 30.06.2010
comment
@Oded getFoo имеет тип short .. Это не проблема   -  person jsmith    schedule 30.06.2010
comment
Возможно, было бы лучше использовать константу для представления «-1» вместо «магических значений», поскольку это обычно облегчает чтение вашего кода.   -  person Matthew Dresser    schedule 30.06.2010
comment
Об этом много раз спрашивали в SO, лучший ответ, который я видел до сих пор: stackoverflow.com/questions/2215745/   -  person Matthew Abbott    schedule 30.06.2010
comment
@jsmith - не удалять. Он будет закрыт и связан с другим вопросом, но если один и тот же вопрос задан по-разному, это хорошо.   -  person ChrisF    schedule 30.06.2010
comment
Это не дубликат. Он спрашивает, почему компилятор не преобразует буквальный -1 в сокращенный. Его ошибка не в условности.   -  person SLaks    schedule 30.06.2010


Ответы (4)


Несколько вещей.

Во-первых, условный оператор - это тернарный оператор, а не третичный оператор.

Во-вторых, я отмечаю, что в ваших образцах кода два образца кода, которые должны быть эквивалентными, не являются:

short foo;
if (isValid)
    foo = -1;
else
   getFoo();

это не то же самое, что

short foo = isValid ? (short)-1 : getFoo();

Первый оставляет foo неназначенным, если isValid имеет значение false. Последний назначает foo независимо от значения isValid.

Я предполагаю, что вы имели в виду

short foo;
if (isValid)
    foo = -1;
else
    foo = getFoo();

и, кроме того, getFoo () возвращает короткий.

Возникает вопрос, почему преобразование в условном операторе без приведения типа является недопустимым, но вследствие того, что оператор if является допустимым.

Это допустимо в выражении if, поскольку в разделе 6.1.9 спецификации указано:

Константное выражение типа int может быть преобразовано в тип sbyte, byte, short, ushort, uint или ulong, при условии, что значение константного выражения находится в пределах диапазона целевого типа.

-1 - это постоянное выражение типа int, которое находится в диапазоне short, поэтому его можно неявно преобразовать в short.

Так почему же форма условного выражения является подделкой?

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

short M(short x){...}
int M(int x){...}

short y = M(-1);

Я не думаю, что вы ожидаете, что разрешение перегрузки скажет хорошо, я бы обычно выбирал M (int), потому что -1 - это int, но нет, я выберу M (short) вместо этого, потому что в противном случае присваивание не будет Работа. Разрешение перегрузки ничего не знает о том, куда идет результат. Его задача - определить правильную перегрузку на основе приведенных аргументов, а не на основе контекста вызова.

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

Итак, мы установили, что тот факт, что это присвоено короткому значению, не имеет значения для определения типа выражения. Но остается вопрос: почему тип условного выражения int, а не короткий?

Это очень хороший вопрос. Перейдем к спец.

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

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

Если существует неявное преобразование из X в Y, но не из Y в X, то Y является типом условного выражения.

Если существует неявное преобразование из Y в X, но не из X в Y, то X - это тип условного выражения.

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

В этом случае оба операнда имеют тип. (Там есть словоблудие о том, имеет ли x тип ... для случая, когда у вас есть null или лямбда; у них нет типов!) Первый операнд имеет тип int, второй - тип short .

Неявное преобразование существует от short к int, но не от int к short. Следовательно, тип условного выражения - int, который нельзя присвоить short.

Можно сказать, что этот алгоритм не так хорош, как мог бы быть. Мы могли бы значительно усложнить алгоритм, чтобы справиться со всеми случаями, когда было два возможных типа кандидатов - в этом случае, int и short являются правдоподобными кандидатами, потому что обе ветви могут быть преобразованы как в int, так и в short , когда рассматриваются как конкретные выражения. , а не просто иметь типы. В этом случае мы могли бы сказать, что предпочтительным был меньший из двух типов.

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

Я подумал о том, чтобы сделать это еще в 2006 году. При разработке поведения того, как LINQ работает с ситуациями, когда есть несколько типов на выбор, и один должен быть выбран как лучший, мы заметили, что условный оператор уже должен был решить эту проблему, и что, кроме того, , в C # 2 это не было реализовано в соответствии со спецификацией. По этому поводу велись долгие споры, и в итоге мы внесли некоторые незначительные изменения в спецификацию условного оператора, чтобы привести его в большее соответствие с его реализованным (и желаемым) поведением. Однако мы решили не вносить большие критические изменения в настройку алгоритма, чтобы использовать меньший из двух возможных типов, когда было несколько на выбор.

Некоторые размышления об этой проблеме см. В моих сообщениях за 2006 год:

person Eric Lippert    schedule 30.06.2010
comment
Спасибо за понимание. Вы были правы в том, что я имел в виду foo = getFoo (); - person jsmith; 30.06.2010

Потому что по умолчанию -1 - целое число. Для компилятора намного безопаснее сказать вам, что вы должны явно указать ему, что делать, чем для компилятора, чтобы сделать предположение относительно того, что вы хотите сделать.

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

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

person kemiller2002    schedule 30.06.2010

Условный оператор, применяемый к int и short, имеет тип int; компилятор не будет выводить тип выражения из типа, которому вы его присвоили.

Вы не можете неявно преобразовать это short выражение в int.

person SLaks    schedule 30.06.2010

Условный оператор требует, чтобы оба возможных результата выражения были одного типа. В этом случае левая сторона - это int, а правая сторона - это short, возвращаемый методом getPoo. Поскольку преобразование короткого замыкания в int всегда безопасно, компилятор выбирает, что результатом операции будет int.

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

Если вы явно используете подход if / else, вы будете назначать буквальное целое число короткому, что позволяет компилятору проверять, что буквальное целое число безопасно назначено короткому без необходимости явного приведения.

Для внутреннего объяснения взгляните на:

Операторы приведения не соблюдают закон о дистрибьюции

person João Angelo    schedule 30.06.2010