При разработке класса, который имеет ссылку на другой объект, может быть полезно создавать ссылочный объект только при первом его использовании, например, использовать ленивую загрузку.
Я часто использую этот шаблон для создания ленивого загруженного свойства:
Encoding utf8NoBomEncoding;
Encoding Utf8NoBomEncoding {
get {
return this.utf8NoBomEncoding ??
(this.utf8NoBomEncoding = new UTF8Encoding(false));
}
}
Затем я наткнулся на этот код при просмотре исходного кода для BCL:
Encoding Utf8NoBomEncoding {
get {
if (this.utf8NoBomEncoding == null) {
var encoding = new UTF8Encoding(false);
Thread.MemoryBarrier();
this.utf8NoBomEncoding = encoding;
}
return this.utf8NoBomEncoding;
}
}
Насколько я могу судить, ни один из них не является потокобезопасным. Например. может быть создано несколько Encoding
объектов. Я полностью понимаю это и знаю, что это не проблема, если создается дополнительный Encoding
объект. Он неизменен и скоро будет сборщиком мусора.
Однако мне действительно любопытно понять, почему Thread.MemoryBarrier
необходим и чем вторая реализация отличается от первой в многопоточных сценариях.
Очевидно, что если безопасность потока является проблемой, то лучшая реализация, вероятно, должна использовать Lazy<T>
:
Lazy<Encoding> lazyUtf8NoBomEncoding =
new Lazy<Encoding>(() => new UTF8Encoding(false));
Encoding Utf8NoBomEncoding {
get {
return this.lazyUtf8NoBomEncoding.Value;
}
}