Введите результат с условным оператором в С#

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

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

class Program
{
    public static void OutputDateTime(DateTime? datetime)
    {
        Console.WriteLine(datetime);
    }

    public static bool IsDateTimeHappy(DateTime datetime)
    {
        if (DateTime.Compare(datetime, DateTime.Parse("1/1")) == 0)
            return true;

        return false;
    }

    static void Main(string[] args)
    {
        DateTime myDateTime = DateTime.Now;
        OutputDateTime(IsDateTimeHappy(myDateTime) ? null : myDateTime);
        Console.ReadLine();                        ^
    }                                              |
}                                                  |
// This line has the compile issue  ---------------+

В строке, указанной выше, я получаю следующую ошибку компиляции:

Тип условного выражения не может быть определен, потому что нет никакого неявного преобразования между '<null> 'и' System.DateTime '

Я запутался, потому что параметр является типом NULL (DateTime?). Зачем ему вообще нужно конвертировать? Если он равен нулю, используйте это, если это время даты, затем используйте это.

У меня создалось впечатление, что

condition ? first_expression : second_expression;

было таким же, как:

if (condition)
   first_expression;
else
   second_expression;

Ясно, что это не так. Каковы причины этого?

(ПРИМЕЧАНИЕ. Я знаю, что если я сделаю "myDateTime" нулевым значением DateTime, тогда он будет работать. Но зачем он нужен?

Как я уже говорил, это надуманный пример. В моем реальном примере "myDateTime" - это сопоставимое по значению значение, которое нельзя сделать допустимым.)

Ответ 1

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

IsDateTimeHappy(myDateTime) ? null : myDateTime;

Так как null и DateTime несовместимы, вам нужно сообщить компилятору, какой тип должен быть. Приведение должно делать трюк:

DateTime? x = IsDateTimeHappy(myDateTime) ? (DateTime?)null : myDateTime;
OutputDateTime(x);

Теперь у компилятора не будет проблем. Вы также можете написать выше на одной строке, если хотите (но я бы, вероятно, не сделал этого):

OutputDateTime(IsDateTimeHappy(myDateTime) ? (DateTime?)null : myDateTime);

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

Ответ 2

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

IsDateTimeHappy(myDateTime) ? null : myDateTime

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

IsDateTimeHappy(myDateTime) ? (DateTime?)null : myDateTime
//OR
IsDateTimeHappy(myDateTime) ? null : (DateTime?)myDateTime

Первая строка кода выше работает, потому что компилятор может преобразовать DateTime в DateTime? с помощью оператора неявного преобразования:

//In Nullable<T>
public static implicit operator T?(T value);

Вторая строка работает, потому что null может быть назначена DateTime?, поскольку последний является ссылочным типом.

Ответ 3

Неявное преобразование не допускается оператором return. Если у вас

if (condition)
    return first_expression;
else
    return second_expression;

Затем вы будете сравнивать яблоки с яблоками. И у вас не будет проблем - как вы заявили.

В вашем случае вам выделено столько места в стеке для DateTime, который является типом значения, не имеющим значения NULL. Поэтому вы делаете выражение, которое не имеет никакого смысла для компилятора. Если вы скажете, я передам вам A или B, тогда A и B должны быть одинаковыми. В вашем случае B никогда не может быть A.

Ответ 5

Что говорит компилятор:

Если IsDateTimeHappy(myDateTime) - false, тогда мне нужно вернуть значение типа DateTime, равное myDateTime. Если это true, тогда мне нужно вернуть значение, равное null, но вы не сказали мне, какой тип он должен быть!

Вот почему ответ "Ответ" - это решение. После того, как вы предоставите команду, сообщающую компилятору, какой тип значения будет возвращен, если условие true, оно может проверить, могут ли возвращаемые значения true и false быть преобразованы в (или имеют) один и тот же тип.

Привет, Марк!; -)

Ответ 6

Вместо null используйте default(DateTime?), и тогда обе стороны тернарного будут иметь совместимые типы.