Выстрел себе в ногу литералом С# по умолчанию и нулевым значением
Что мне нравится в 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
... не значение смещения тысячи лет назад.
Это было невероятно сложно отлаживать, поэтому я решил написать об этом на случай, если это поможет кому-то еще с похожей проблемой.
Только полноправные пользователи могут оставлять комментарии. Аутентифицируйтесь пожалуйста, используя сервисы.