2

When I want to make use of Lazy<T> and need to refer to this I need to write a lot of boilerplate code:

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

Is this all really necessary? It feels like too much boilerplate and everything all over the place. Are there any shortcuts that read a bit nicer with not that much seperate boilerplate around? I could probably move the body to the constructor, but I wouldn't like that either - i.e. you move a lot of important logic into your constructor.

My preferred way would be something similar like 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;
})

Not a lot of 'moving parts' here and super concise. What other options are there? I notice that I often fall back to a manual 'lazy' construction.

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

At least this has less 'moving parts' then the Lazy way, and I can keep everything together (do not have to put half the logic in the constructor). Of course this has a lot of other disadvantages, like that it's not thread-safe.

Am I still missing other alternatives?

PS I assume there are in a two different answers - one for true thread-safe lazy loading and one for just a concise version where you don't care if it accidently gets called twice.

Dirk Boer
  • 7,007
  • 9
  • 49
  • 89
  • `lazyComputed` is not the same as `Lazy` in C#. For example, `Lazy` runs at most once (unlike `lazyComputed` which re-runs if the dependencies change). – mjwills Nov 12 '18 at 23:46
  • I feel this warrants somewhat of an opinionated response. I often use this same approach of `manual` lazy construction. I think it depends on the case: https://stackoverflow.com/questions/6847721/when-should-i-use-lazyt – adam Nov 12 '18 at 23:53
  • I don't think you are going to get it much more concise than your original code. – mjwills Nov 12 '18 at 23:54
  • "*is this all really necessary? It feels like too much boilerplate and everything all over the place*" sounds like a case of Code OCD and syntax envy. If you want to use Lazy you are stuck with this pattern. Also Knockout.js / TypeScript are a completely different beast, there are fundamental reasons why they can do those shenanigans, and the CLR disallows it in .Net – TheGeneral Nov 12 '18 at 23:54
  • Before `Lazy` we had locking and booleans to see if stuff was initialized. That was ugly. Even without `Lazy` you'd still need a lot of that code. One alternative is a cache interceptor. Then your class just does its thing and doesn't care about when or how often it executes. That's also useful if you want the instantiated value to expire. – Scott Hannen Nov 13 '18 at 02:34
  • 1
    For the record, I'd personally use `LazyWithNoExceptionCaching` rather than `Lazy` - but that won't benefit your conciseness. https://stackoverflow.com/a/42567351/34092 – mjwills Nov 13 '18 at 03:00
  • What does `SubEventCollection` need from `Event`? Could you feasibly rewrite it to not need `Initialize` to be called? – TheHans255 Nov 17 '18 at 23:39

2 Answers2

0

I would suggest moving Lazy from the internals of the class to how the class is used in methods. Eagerly initialize Event within the body of the class (including its SubEventCollection), but instead of using Event outside of that, use Lazy<Event>.

So, declare:

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

But then, instead of returning an Event from whatever is producing an event, return a Lazy<Event>, offering them the ability to return more data as needed. This also has the advantage of informing users of Event that getting event data is a potentially expensive operation.

TheHans255
  • 1,808
  • 15
  • 29
-1

At the moment I'm trying out an own implementation - still needs to be reviewed.

Class:

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

Use:

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

Advantages:

  • I can keep all the relevant code together (don't have to put half in the constructor)
Dirk Boer
  • 7,007
  • 9
  • 49
  • 89
  • "half in the constructor"? _9%_, actually, and you introduce the risk of accidentally using an incorrect combination of `MyLazy` instance and `create` function. Absolutely not worth it, in my oppinion. – Haukinger Nov 15 '18 at 15:53
  • Okay, half of the **boilerplate code**. And then even the most critical part. I don't like that (especially if you have +5 properties in there), but I respect that you have a different opinion. Live and let live. Too bad for the negative downvoting (if the implementation is correct), but peace! <3 – Dirk Boer Nov 15 '18 at 18:48