Когда я хочу использовать Lazy<T> и мне нужно обратиться к this, мне нужно написать много шаблонного кода:
// the private member
private Lazy<SubEventCollection> _SubEvents;
public Event()
{
// needs to be initialized in the constructor because I refer to this
_SubEvents = new Lazy<SubEventCollection3>(CreateSubEvents);
}
// the "core" body
private SubEventCollection CreateSubEvents()
{
SubEventCollection3 collection;
using ( var stream = new MemoryStream(DbSubEventsBucket) )
collection = Serializer.Deserialize<SubEventCollection3>(stream);
collection.Initialize(this);
return collection;
}
// The final property
public SubEventCollection SubEvents => _SubEvents.Value;
Это все действительно необходимо? Кажется, что слишком много шаблонов и всего в одном месте. Есть ли какие-то ярлыки, которые читаются немного лучше, когда вокруг не так много отдельного шаблона? Я, наверное, мог бы переместить тело в конструктор, но мне это тоже не понравилось бы - то есть вы перемещаете много важной логики в свой конструктор.
Я предпочел бы что-то похожее на Knockout.js / TypeScript.
subEvents = ko.lazyComputed(() =>
{
SubEventCollection3 sub_events;
using ( var stream = new MemoryStream(DbSubEventsBucket) )
sub_events = Serializer.Deserialize<SubEventCollection3>(stream);
sub_events.Initialize(this);
return sub_events;
})
Здесь не так много «движущихся частей» и очень лаконично. Какие еще есть варианты? Я замечаю, что часто возвращаюсь к «ленивому» построению вручную.
private SubEventCollection _SubEvents;
public SubEventCollection SubEvents
{
get
{
if ( _SubEvents == null )
{
using ( var stream = new MemoryStream(DbSubEventsBucket) )
collection = Serializer.Deserialize<SubEventCollection3>(stream);
collection.Initialize(this);
_SubEvents = collection;
}
return _SubEvents;
}
}
По крайней мере, в нем меньше «движущихся частей», чем в ленивом, и я могу держать все вместе (не нужно помещать половину логики в конструктор). Конечно, у этого есть много других недостатков, например, он не является поточно-ориентированным.
Мне все еще не хватает других альтернатив?
PS Я предполагаю, что есть два разных ответа - один для истинной потокобезопасной ленивой загрузки и один для просто краткой версии, где вам все равно, если она случайно будет вызвана дважды.
Я считаю, что это требует некоторой самоуверенной реакции. Я часто использую тот же подход ленивого построения manual. Думаю, это зависит от случая: stackoverflow.com/questions/6847721/when-should-i-use-lazyt
Я не думаю, что вы собираетесь сделать его намного более лаконичным, чем исходный код.
"это все действительно необходимо? Кажется, что слишком много шаблонов и всего повсюду" звучит как случай зависти от кода OCD и синтаксиса. Если вы хотите использовать Lazy <T>, вы застряли с этим шаблоном. Также Knockout.js / TypeScript - совершенно другой зверь, есть фундаментальные причины, по которым они могут делать эти махинации, и CLR запрещает это в .Net
До Lazy<T> у нас были блокировки и логические значения, чтобы проверить, инициализирован ли материал. Это было некрасиво. Даже без Lazy вам все равно понадобится много этого кода. Одна альтернатива - перехватчик кеша. Тогда ваш класс просто делает свое дело и не заботится о том, когда и как часто он выполняется. Это также полезно, если вы хотите, чтобы срок действия созданного значения истек.
Для протокола, я лично использовал бы LazyWithNoExceptionCaching, а не Lazy, но это не повлияет на вашу лаконичность. stackoverflow.com/a/42567351/34092
Что нужно SubEventCollection от Event? Не могли бы вы его реально переписать так, чтобы для вызова Initialize не требовалось?





На данный момент я пробую собственную реализацию - все еще требует доработки.
Класс:
/// <summary>
/// Warning: might not be as performant (and safe?) as the Lazy<T>, see:
/// https://codereview.stackexchange.com/questions/207708/own-implementation-of-lazyt-object
/// </summary>
public class MyLazy<T>
{
private T _Value;
private volatile bool _Loaded;
private object _Lock = new object();
public T Get(Func<T> create)
{
if ( !_Loaded )
{
lock (_Lock)
{
if ( !_Loaded ) // double checked lock
{
_Value = create();
_Loaded = true;
}
}
}
return _Value;
}
public void Invalidate()
{
lock ( _Lock )
_Loaded = false;
}
}
Использовать:
MyLazy _SubEvents = new MyLazy();
public SubEventCollection SubEvents => _SubEvents.Get(LoadSubEvents);
private SubEventCollection LoadSubEvents()
{
using ( var stream = new MemoryStream(DbSubEventsBucket) )
{
var sub_event_collection = Serializer.Deserialize<SubEventCollection>(stream);
sub_event_collection.Initialize(this);
return sub_event_collection;
}
}
Преимущества:
«половина в конструкторе»? Фактически, 9%, и вы вводите риск случайного использования неправильной комбинации экземпляра MyLazy и функции create. На мой взгляд, абсолютно того не стоит.
Хорошо, половина шаблонный код. А потом даже самая важная часть. Мне это не нравится (особенно, если у вас там +5 объектов недвижимости), но я уважаю, что вы придерживаетесь другого мнения. Живи и давай жить другим. Жаль, что отрицательное голосование против (если реализация верна), но мир! <3
Я бы посоветовал переместить Lazy из внутренней части класса в то, как этот класс используется в методах. С нетерпением инициализируйте Event в теле класса (включая его SubEventCollection), но вместо использования Event вне его используйте Lazy<Event>.
Итак, заявляем:
public class Event
{
public SubEventCollection SubEvents { get; private set; }
public Event()
{
using ( var stream = new MemoryStream(DbSubEventsBucket) )
SubEvents = Serializer.Deserialize<SubEventCollection3>(stream);
SubEvents.Initialize(this);
}
}
Но затем вместо того, чтобы возвращать Event из того, что создает событие, верните Lazy<Event>, предлагая им возможность возвращать больше данных по мере необходимости. Это также имеет то преимущество, что информирует пользователей Event о том, что получение данных о событиях - потенциально дорогостоящая операция.
lazyComputed- это не то же самое, чтоLazyв C#. Например,Lazyзапускается не более одного раза (в отличие отlazyComputed, который запускается повторно, если зависимости меняются).