Проблема типа Nullable с?: Условный оператор

154

Может кто-нибудь объяснить, почему это работает в C # .NET 2.0:

    Nullable<DateTime> foo;
    if (true)
        foo = null;
    else
        foo = new DateTime(0);

... но это не так:

    Nullable<DateTime> foo;
    foo = true ? null : new DateTime(0);

Последняя форма дает мне ошибку компиляции: «Тип условного выражения не может быть определен, поскольку не существует неявного преобразования между« <null> »и« System.DateTime ».»

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

Ник Готч
источник
12
Вы можете сэкономить много печатать с помощью DateTime? вместо Nullable <DateTime>.
Стюарт Джонсон

Ответы:

325

Этот вопрос уже задавался несколько раз. Компилятор говорит вам, что он не знает, как конвертировать nullв DateTime.

Решение простое:

DateTime? foo;
foo = true ? (DateTime?)null : new DateTime(0);

Обратите внимание, что Nullable<DateTime>можно написать, DateTime?что сэкономит вам кучу печатать.

Стюарт Джонсон
источник
Работает достаточно хорошо, но теперь вы не можете проверить foo на ноль - оно всегда будет иметь значение. Хотя нет никакого способа обойти это - как говорит MojoFilter: «Это потому, что в троичном операторе два значения должны быть одного типа».
DilbertDave
@DilbertDave Информация из поста MojoFilter неверна.
Mishax
4
Я бы добавил, что компилятор пытается угадать результирующий тип тернарной операции, не глядя на переменную, которой она назначена, а взглянув на операнды. Он находит, <null>и DateTimeвместо того, чтобы найти общий тип предка, он просто пытается найти преобразование между собой. (Дополнительный бит: C # распознает <null>тип, то есть тип каждого nullвыражения.)
IllidanS4 хочет вернуть Монику
Если это уже задавали много раз, где флаг дубликата вопроса?
starmandeluxe
@starmandeluxe все они, вероятно, указывают здесь (по крайней мере, я, как я попал сюда)
Скотт Чемберлен
19

К вашему сведению (оффтоп, но изящный и относящийся к обнуляемым типам) у нас есть удобный оператор только для обнуляемых типов, называемый оператором слияния нуль

??

Используется так:

// Left hand is the nullable type, righthand is default if the type is null.
Nullable<DateTime> foo;
DateTime value = foo ?? new DateTime(0);
оборота FlySwat
источник
9
Как это отвечает на его вопрос?
Стюарт Джонсон
3
Ник пытается присвоить foo значение null, если какое-либо условие выполняется. Нулевое объединение назначит DateTime (0) значение, если foo равно нулю. Два совершенно не связаны.
Джером Ирвин
4
Следовательно, к вашему сведению, оффтоп, но приятно знать.
FlySwat
Ах хорошо. Это довольно полезно знать.
Джером Ирвин
8

Это связано с тем, что в тернарном операторе два значения должны разрешаться в один и тот же тип.

MojoFilter
источник
10
Нет, они не должны быть одного типа. Либо второй операнд должен быть неявно конвертируемым в тип третьего операнда, либо наоборот.
Mishax
3

Я знаю, что этот вопрос задавался в 2008 году, а сейчас - 5 лет спустя, но ответ, помеченный как ответ, меня не удовлетворяет. Реальный ответ заключается в том, что DateTime является структурой, и как структура она не совместима с нулем. У вас есть два пути решения этого:

Во-первых, сделать NULL совместимым с DateTime (например, привести NULL к DateTime?, Как предполагает джентльмен с 70 голосами вверх, или NULL для Object или ValueType).

Второе - сделать DateTime совместимым с нулевым значением (например, приведение DateTime к DateTime?).

Mishax
источник
3

Другое решение, подобное принятому, заключается в использовании defaultключевого слова C # . Хотя он определен с использованием обобщений, он фактически применим к любому типу.

Пример использования, примененный к вопросу ОП:

Nullable<DateTime> foo;
foo = true ? default(DateTime) : new DateTime(0);

Пример использования с текущим принятым ответом:

DateTime? foo;
foo = true ? default(DateTime) : new DateTime(0);

Кроме того, используя default, вам не нужно указывать переменную как nullableдля того, чтобы присвоить ей nullзначение. Компилятор автоматически назначит значение по умолчанию для определенного типа переменной, и ошибки не возникнет. Пример:

DateTime foo;
foo = true ? default(DateTime) : new DateTime(0);
newfurniturey
источник
13
Не правда, default(DateTime)не ноль, это " 1.1.0001 0:00:00", так же, как new DateTime(0).
IllidanS4 хочет вернуть Монику
@ IllidanS4, я не говорил, что он равен null, только то, что с помощью default()него можно присвоить nullableзначение (как указано в MSDN). Примеры я показываю продемонстрировать универсальность , что он может быть использован с Nullable<DateTime>, DateTime?и просто DateTime. Если вы считаете, что это неверно, можете ли вы предоставить PoC, если это не помогло?
newfurniturey
3
Ну, спрашивающий хотел сохранить nullв переменной, нет default(DateTime), так что это в лучшем случае вводит в заблуждение. Это не «универсальный» , как вы подразумеваете, так как выражение в целом имеет все тот же тип - DateTimeи вы можете заменить default(DateTime)с , new DateTime()и он будет делать то же самое. Может быть, default(DateTime?)это то, что вы имели в виду, так как это на самом деле равно null.
IllidanS4 хочет вернуть Монику