Условный оператор не может действовать неявно?

Я немного взволнован этой маленькой чертой С#:

Данные переменные:

Boolean aBoolValue;
Byte aByteValue;

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

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

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

aByteValue = aBoolValue ? 1 : 0;

Ошибка говорит: "Невозможно неявно преобразовать тип" int "в" byte ".

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

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

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

EDIT:

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

Ответы

Ответ 1

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

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

x = y;

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

Это потому, что может быть больше одного 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). Поскольку он не является константой, вы не можете назначить его байту; компилятор ссылается исключительно на типы, а не на значения, когда результат не является константой.

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

Ответ 2

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

 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);

Итак, да, кажется, что тернарный оператор заставляет константы "фиксировать" их типы как ints и отключать неявное преобразование типа, которое вы в противном случае получали бы от констант, которые соответствуют меньшему типу.

Ответ 3

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

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

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

EDIT: с помощью readonly не имеет смысла - они никогда не должны меняться.