2010-06-30 8 views
3

Можно создать дубликат:
Conditional operator cannot cast implicitly?Зачем мне приписывать 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, который нужно перевести в короткий. Но почему?

+0

Вы могли бы выбрать лучшие имена переменных и функций ... – Oded

+0

Каков тип возврата 'getFoo'? – Oded

+0

@Oded getFoo имеет тип короткий ..Это не проблема – jsmith

ответ

11

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

Прежде всего, условный оператор является 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 года на нем:

+0

Благодарим вас за понимание. Вы были правы в том, что я имел в виду foo = getFoo(); – jsmith

6

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

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

В качестве примечания вы должны зайти в блог Eric Lippert's, так как я знаю, что он объясняет, почему компилятор не делает таких предположений.

1

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

Вы не можете неявно отнести это выражение short к int.

0

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

Таким образом, результатом будет присвоение intshort, и именно поэтому вам необходимо явно указать его на короткий.

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

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

Cast operators do not obey the distributive law