Условный оператор не может быть приведен неявно? - PullRequest
59 голосов
/ 07 февраля 2010

Я немного озадачен этой маленькой причудой C #:

Заданные переменные:

Boolean aBoolValue;
Byte aByteValue;

Следующие компиляции:

if (aBoolValue) 
    aByteValue = 1; 
else 
    aByteValue = 0;

Но это не будет:

aByteValue = aBoolValue ? 1 : 0;

Ошибка говорит: «Не удается неявно преобразовать тип« int »в« байт »."

И, конечно, это чудовище скомпилирует:

aByteValue = aBoolValue ? (byte)1 : (byte)0;

Что здесь происходит?

EDIT:

Использование VS2008, C # 3.5

Ответы [ 3 ]

67 голосов
/ 07 февраля 2010

Это довольно часто задаваемый вопрос.

В C # мы почти всегда рассуждаем изнутри наружу. Когда вы видите

x = y;

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

Это потому, что может быть более одного x:

void M(int x) { }
void M(string x) { }
...
M(y); // y is assigned to either int x or string x depending on the type of y

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

Чтобы определить тип условного выражения, мы определяем тип следствия и альтернативные выражения, выбираем более общий из двух типов, и это становится типом условного выражения. Итак, в вашем примере тип условного выражения - «int», и он не является константой (если только выражение условия не является константой true или константой false). Поскольку это не константа, вы не можете присвоить ее байту; компилятор рассуждает исключительно из типов, а не из значений, когда результат не является константой.

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

11 голосов
/ 07 февраля 2010

Я использую VS 2005, для и могу воспроизвести, для bool & Boolean, но не для true

 bool abool = true;
 Boolean aboolean = true;
 Byte by1 = (abool ? 1 : 2);    //Cannot implicitly convert type 'int' to 'byte'
 Byte by2 = (aboolean ? 1 : 2); //Cannot implicitly convert type 'int' to 'byte'
 Byte by3 = (true ? 1 : 2);     //Warning: unreachable code ;)

Самый простой обходной путь, кажется, это приведение

 Byte by1 = (Byte)(aboolean ? 1 : 2);

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

7 голосов
/ 07 февраля 2010

У меня может не быть хорошего ответа для вас, но если вы делаете это во многих местах, вы можете заявить:

private static readonly Byte valueZero = (byte)0;
private static readonly Byte valueOne = (byte)1;

и только эти переменные. Вы можете использовать const, если он локальный для проекта.

РЕДАКТИРОВАТЬ: с использованием readonly не имеет смысла - они никогда не предназначены для изменения.

...