Выстрел себе в ногу литералом С# по умолчанию и нулевым значением
Что мне нравится в C#, так это то, что разработчики языка постоянно совершенствуют его, поэтому мы регулярно получаем новый синтаксис для экспериментов. Как правило, я очень хочу использовать эти новые функции, поскольку они обычно помогают нам писать более чистый и лаконичный код.
Однако я хотел бы выделить то, что меня зацепило: использование литерала по умолчанию в сочетании со значениями, допускающими значение NULL.
Это не вина самой языковой функции, а скорее случай, когда я применяю ее вслепую, не задумываясь об этом сначала, что всегда опасно!
defaultБуквальный _
У нас есть выражения значений по умолчанию , например default(int), уже давно*. Вот надуманный пример, который присваивает значение по умолчанию для int( 0) переменной, если логическое значение setValueравно false:
var setValue = false;
var intValue = 123;
var valueOrDefault = setValue ? intValue : default(int);Начиная с C# 7.1 литерал по умолчанию может определять тип:
var valueOrDefault = setValue ? intValue : default;Обратите внимание, что тип выводится из типа, возвращаемого другой ветвью условия ( value), а не из типа переменной, которую вы присваиваете ; в любом случае это не может быть в этом случае, поскольку это объявлено с помощью var.
Как я выстрелил себе в ногу...
Я создавал очень простую систему аутентификации на основе файлов cookie в веб-приложении .NET Core. В Loginметод контроллера я добавил следующее:
// Preceding code omitted for brevity
var principal = new ClaimsPrincipal(identity);
var authenticationProperties = new AuthenticationProperties {
    IsPersistent = model.RememberMe,
    ExpiresUtc = model.RememberMe 
        ? DateTimeOffset.UtcNow.AddDays(7) 
        : default
};
await HttpContext.SignInAsync(principal, authenticationProperties);Я ожидаю, что некоторые из вас уже заметили ошибку... но если нет, читайте дальше!
AuthenticationProperties.ExpiresUtcопределяет, как долго файл cookie аутентификации висит в браузере клиента. если пользователь установит флажок «Запомнить меня», я хочу, чтобы срок его действия истек через 7 дней; если нет, время истечения следует оставить по умолчанию.
ExpiresUtcявляется DateTimeOffset?, поэтому defaultзначение будет null. Это приведет к тому, что файл cookie будет установлен как файл cookie сеанса, срок действия которого истекает при закрытии браузера.
Однако, несмотря на то, что код читается так, он ведет себя иначе !
Как я уже упоминал, defaultлитерал выводит тип из значения, возвращаемого другой ветвью троичного условного оператора, в данном случае это DateTimeOffset.UtcNow.AddDays(7).
Это не обнуляемый DateTimeOffset? , а базовый DateTimeOffset: значение по умолчанию равно DateTimeOffset.MinValue.
Итак... если пользователь не отмечает "Запомнить меня", мой код устанавливается ExpiresUtcна 01/01/0001 00:00:00 +00:00. Это означает, что срок действия куки-файла аутентификации немедленно истекает, и даже несмотря на то, что их вход в систему был действительно успешным, пользователь перенаправляется прямо обратно на страницу входа.
Правильный путь
Правильный способ сделать это — использовать выражение значения по умолчанию , указав тип:
var authenticationProperties = new AuthenticationProperties {
    IsPersistent = model.RememberMe,
    ExpiresUtc = model.RememberMe 
        ? DateTimeOffset.UtcNow.AddDays(7) 
        : default(DateTimeOffset?)
};Теперь мы используем DateTimeOffset?значение по умолчанию, что означает, что значение по умолчанию на самом деле null... не значение смещения тысячи лет назад. 
Это было невероятно сложно отлаживать, поэтому я решил написать об этом на случай, если это поможет кому-то еще с похожей проблемой.
 
                                
Только полноправные пользователи могут оставлять комментарии. Аутентифицируйтесь пожалуйста, используя сервисы.