TL; DR: Если вы удалите аргумент initialized
, то EnsureInitialized()
будет гарантировать, что возвращаемый объект [NotNull]
- даже если вы передадите унифицированный MyClass?
ссылка - и, следовательно, нет необходимости использовать оператор, допускающий нулевое значение (!
).
Полный ответ
Это хороший вопрос. Ответ на ваш пример specifici c действительно прост, но я также хочу воспользоваться возможностью, чтобы ответить на ваш общий вопрос о том, как обрабатывать ref
аргументы с помощью C# Ссылочные типы 8.0, допускающие значение NULL. При этом это не только поможет решить другой сценарий ios, подобный этому, но также предложит объяснение , почему работает решение для вашего конкретного c примера.
Specifi c Пример
Хотя документация EnsureInitialized()
не проясняет это полностью, конкретная перегрузка, которую вы вызываете, предназначена для ситуаций, когда вы потенциально хотите a null
цель. А именно, если вы вместо этого передадите значение true
для параметра initialized
, тогда он вернет null
. Это условие является причиной того, что должен возвращать тип, допускающий значение NULL.
Поскольку вы не хотите разрешать null
значения - и не работаете со значением type - вам просто нужно удалить аргумент initialized
. Вам все равно нужно будет объявить свой _initializationTarget
как допускающий значение NULL, поскольку он не инициализируется в вашем конструкторе. Эта перегрузка, однако, гарантирует компилятору, что параметр target
больше не будет иметь значение null
после выполнения EnsureInitialized()
:
private static object _initializationLock = new object();
private static MyClass? _initializationTarget; //marked as nullable
public MyClass GetInstance() =>
LazyInitializer.EnsureInitialized(
ref _initializationTarget,
ref _initializationLock,
() => new MyClass()
);
Обратите внимание, что пока он все еще передает значение null (может) MyClass?
, он уверенно возвращает MyClass
без необходимости прибегать к оператору , допускающему нулевые значения (!
).
Общий вопрос
При погружении Далее в ссылочных типах C# 8.0, допускающих значение NULL, вы обнаружите ряд пробелов в анализе потока c Roslyn, которые нельзя устранить неоднозначностью, просто используя только операторы ?
и !
. К счастью, Microsoft предвидела эту проблему и предоставила нам множество атрибутов , которые можно использовать для предоставления подсказок компилятору .
Пример
Вот базовый пример c для иллюстрируют общую проблему:
public void EnsureNotNull(ref Object? input) => input ??= new Object();
Если вы вызовете этот метод с помощью следующего кода, вы получите предупреждение CS8602
:
Object? object = null;
EnsureNotNull(ref object);
_ = object.ToString(); //CS8602; Dereference of a possibly null reference
Однако вы можете смягчить это, применив подсказку [NotNull]
к параметру input
:
public void EnsureNotNull([NotNull]ref Object? input) => input ??= new Object();
Теперь любые ссылки на object
после вызова EnsureNotNull()
будут известны (компилятором ), чтобы не быть null
.
EnsureInitialized()
перегрузок
Если вы оцениваете исходный код LazyInitializer
с учетом вышеизложенного, ответ на ваш specifici c проблема становится намного яснее. Перегрузка , которую вы вызывали , отмечает target
как [AllowNull]
, что эквивалентно возврату MyClass?
:
public static T EnsureInitialized<T>([AllowNull] ref T target, ref bool initialized, [NotNull] ref object? syncLock, Func<T> valueFactory) => …
Напротив, перегрузка, которую я ' Рекомендуемый реализует атрибут [NotNull]
, описанный выше, что эквивалентно возврату MyClass
:
public static T EnsureInitialized<T>([NotNull] ref T? target, [NotNull] ref object? syncLock, Func<T> valueFactory) where T : class => …
Если вы оцените фактическое значение logi c, вы увидите, что они в основном то же самое, за исключением того, что первое включает предложение escape для сценария, где initialized
равно true
- и, следовательно, позволяет target
потенциально оставаться null
.
Поскольку вы знаете , что вы работаете с классом, и не хотите значение null
, однако последняя перегрузка является лучшим выбором и реализует точную практику, изложенную в моем ответе на ваш общий вопрос.