-1

Is there a way in the C# standard API to lazily initialize a block of code?

I know Lazy<T> but it is meant to initialize one variable, and requires a function that returns a T. I'd like something that's more like a LazyAction.

private _lazyInit = new LazyAction( () => {
    ...
    // Do something big like get some data from DB
    ...
    _myField1 = ...
    _myField2 = ...
    ...
    do no return stuff.
}

public object get_Field1() { _lazyInit.EnsureDone(); return _myfield1; }
public object get_Field2() { _lazyInit.EnsureDone(); return _myfield2; }

For thread-safety, the LazyAction would have some mechanism to ensure it's only run once.

edeboursetty
  • 5,669
  • 2
  • 40
  • 67
  • why just make a Lazy and just return something/anything? then it'll only run once and not care what it returns so long as it returns something. You can even wrap around voids if you wanted to by just calling the void and then returning "1" – Peter Karman Oct 10 '19 at 18:22
  • 2
    Factor out all of this stuff that can be initialized ("block") in its own class and have a `Lazy` field. Then the properties just become `public object Field1 => foo.Value.Field1`. – Jeroen Mostert Oct 10 '19 at 18:25
  • @PeterKarman: sure that's an option that works. But that's kind of hackish, so I was wondering if there was a standard way. I could also implement the class myself... – edeboursetty Oct 10 '19 at 18:28
  • @JeroenMostert: sure that works too. I mean: I know how to find ways to do this. But it seems that it's something that should be common enough to exist in the standard library... Maybe they don't like the side effect part. – edeboursetty Oct 10 '19 at 18:31
  • Perhaps you want just an [Action](https://learn.microsoft.com/en-us/dotnet/api/system.action?view=netframework-4.8) initialized with a lambda expression? – mason Oct 10 '19 at 18:33
  • @mason: an action would run twice if I call `Field1` and `Field2`. I need that the method run only once. – edeboursetty Oct 10 '19 at 18:34
  • 2
    I wouldn't like side effects either. The whole point of `Lazy` is to give you customizable thread-safe initialization. Now mix in side effects and see how un-obvious that becomes. If you really want this, the implementation's trivial enough if you don't want bells and whistles -- a simple wrapper class that uses `Interlocked.CompareExchange` to ensure the delegate's called but once. You don't even really need the wrapper if you have only one such method. – Jeroen Mostert Oct 10 '19 at 18:36
  • 3
    Having a long-running operation (DB access) in a property getter is a design flaw. Properties [should be fast](https://learn.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/ms229054(v=vs.100)?redirectedfrom=MSDN). – dymanoid Oct 10 '19 at 19:01
  • @dymanoid: Fine, the question holds with methods, I'll edit. – edeboursetty Oct 11 '19 at 15:04

1 Answers1

1

Although I like Jeroen Mostert's proposal better, you could do something like this:

create a class that holds your initialization method:

class LazyInitializer
{
    private readonly Action initFunc;

    class State { public bool Initialized = false; }

    public LazyInitializer(Action initFunc)
    {
        this.initFunc = initFunc;
    }

    public Action CreateInitializer()
    {
        var state = new State();

        return () =>
        {
            lock (state)
            {
                if (state.Initialized == false)
                {
                    initFunc();
                    state.Initialized = true;
                }
            }
        };
    }
}

and then use it like:

var lazyInit = new LazyInitializer(() =>
{
    //here your initialization code
    ...
    _myField1 = ...
    _myField2 = ...
    ...
});

//Create the initializer action
var initialize = lazyInit.CreateInitializer();

//use it like:
public object get_Field1() { initialize(); return _myfield1; }
public object get_Field2() { initialize(); return _myfield2; }
Alberto
  • 15,626
  • 9
  • 43
  • 56
  • Note that holding a critical section while executing unknown user code might lead to deadlocks and other unwanted side effects. – dymanoid Oct 11 '19 at 16:49