Почему требуется явное преобразование после проверки на null?

int foo;
int? bar;

if (bar != null)
{
    foo = bar; // does not compile
    foo = (int)bar; // compiles
    foo = bar.Value; // compiles
}

Я давно знаю, что первое утверждение неверно, но оно всегда меня било. Я проверял, что bar не является нулевым, так почему компилятор жалуется?

Ответ 1

Тип bar все еще int?, и нет никакого неявного преобразования от int? до int.

Условие не меняет срок действия более позднего кода. То же самое относится и к другим приведениям:

object x = ...;
if (x is string)
{
    string y = x; // This is still invalid
    string z = (string) x; // This is fine
} 

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

bool condition = ...;
string x;
if (condition)
{
    x = "yes";
}
if (!condition)
{
    x = "no";
}
Console.WriteLine(x); // Invalid

Последняя строка недействительна, поскольку x по-прежнему не определен. Мы знаем, что независимо от значения x, мы введем одно из этих операторов выражения if... но компилятор не пытается понять это.

Хотя это может показаться глупым, это значительно упрощает правила языка.

Ответ 2

Сравнение говорит только: это не null, компилятор все еще использует тип, чтобы узнать, можно ли назначить назначение.

После этого компиляция даже без нулевой проверки.

foo = (int)bar; 

Ответ 3

Компилятор проверяет, является ли ваша программа синтаксически правильной. Он не заботится о вашей нулевой проверке.

Компилятор видит, что вы можете потерять информацию, присваивающую int? к int, и поэтому он жалуется.

Ответ 4

Представьте себе: что, если у вас была переменная-член, введенная как Nullable<int>, к которой был обращен несколько потоков. Давайте посмотрим на ваш код при таких обстоятельствах.

if(foo != null)
{
    // expensive operation that takes time.
    // what if another thread as nulled foo in the meantime?
    int bar = foo;
}