Краткий способ записать Lazy загруженные свойства в C # - PullRequest
0 голосов
/ 13 ноября 2018

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

Ответы [ 2 ]

0 голосов
/ 18 ноября 2018

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

0 голосов
/ 15 ноября 2018

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

Класс:

/// <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;
    }
}

Преимущества:

  • Я могу хранить весь соответствующий код вместе (не нужно помещать половину в конструктор)
...