Краткий способ записи свойств с отложенной загрузкой в ​​C#

Когда я хочу использовать 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 Я предполагаю, что есть два разных ответа - один для истинной потокобезопасной ленивой загрузки и один для просто краткой версии, где вам все равно, если она случайно будет вызвана дважды.

lazyComputed - это не то же самое, что Lazy в C#. Например, Lazy запускается не более одного раза (в отличие от lazyComputed, который запускается повторно, если зависимости меняются).
mjwills 13.11.2018 00:46

Я считаю, что это требует некоторой самоуверенной реакции. Я часто использую тот же подход ленивого построения manual. Думаю, это зависит от случая: stackoverflow.com/questions/6847721/when-should-i-use-lazyt

adam 13.11.2018 00:53

Я не думаю, что вы собираетесь сделать его намного более лаконичным, чем исходный код.

mjwills 13.11.2018 00:54

"это все действительно необходимо? Кажется, что слишком много шаблонов и всего повсюду" звучит как случай зависти от кода OCD и синтаксиса. Если вы хотите использовать Lazy <T>, вы застряли с этим шаблоном. Также Knockout.js / TypeScript - совершенно другой зверь, есть фундаментальные причины, по которым они могут делать эти махинации, и CLR запрещает это в .Net

TheGeneral 13.11.2018 00:54

До Lazy<T> у нас были блокировки и логические значения, чтобы проверить, инициализирован ли материал. Это было некрасиво. Даже без Lazy вам все равно понадобится много этого кода. Одна альтернатива - перехватчик кеша. Тогда ваш класс просто делает свое дело и не заботится о том, когда и как часто он выполняется. Это также полезно, если вы хотите, чтобы срок действия созданного значения истек.

Scott Hannen 13.11.2018 03:34

Для протокола, я лично использовал бы LazyWithNoExceptionCaching, а не Lazy, но это не повлияет на вашу лаконичность. stackoverflow.com/a/42567351/34092

mjwills 13.11.2018 04:00

Что нужно SubEventCollection от Event? Не могли бы вы его реально переписать так, чтобы для вызова Initialize не требовалось?

TheHans255 18.11.2018 00:39
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
7
874
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

На данный момент я пробую собственную реализацию - все еще требует доработки.

Класс:

/// <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. На мой взгляд, абсолютно того не стоит.

Haukinger 15.11.2018 16:53

Хорошо, половина шаблонный код. А потом даже самая важная часть. Мне это не нравится (особенно, если у вас там +5 объектов недвижимости), но я уважаю, что вы придерживаетесь другого мнения. Живи и давай жить другим. Жаль, что отрицательное голосование против (если реализация верна), но мир! <3

Dirk Boer 15.11.2018 19:48

Я бы посоветовал переместить 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 о том, что получение данных о событиях - потенциально дорогостоящая операция.

Другие вопросы по теме