Francesco Bonizzi
Francesco Bonizzi

Reputation: 5302

Encapsulate Lazy<T>

After a number of times I need to implement a Lazy<T> pattern, it came to my mind the idea to put the whole pattern into a class in order to hide Lazy implementation.

In the case of "Loading a singleton configuration object for my services", I did something like this:

public interface IConfigurationProvider<T>
{
    T GetConfiguration();
}

public abstract class SingletonConfigurationProvider<TConfiguration> : IConfigurationProvider<TConfiguration>
{
    private static readonly Lazy<TConfiguration> _lazy =
       new Lazy<TConfiguration>(_loadConfiguration);

    private static Func<TConfiguration> _loadConfiguration;

    private SingletonConfigurationProvider() { }
    public SingletonConfigurationProvider(Func<TConfiguration> LoadConfig)
    {
        _loadConfiguration = LoadConfig;
    }

    public TConfiguration GetConfiguration()
    {
        return _lazy.Value;
    }
}

My goal is to obtain, from the "outside" the simplicity in doing this:

public class ConfigTest : SingletonConfigurationProvider<ObjectTest> 
{
    public ConfigTest()
        : base(Load)
    {
    }

    public static ObjectTest Load()
    {
        return new ObjectTest()
        {
            testInt = 3,
            testString = "Hi"
        };
    }
}

The points are:

Upvotes: 0

Views: 107

Answers (1)

Steve Cooper
Steve Cooper

Reputation: 21480

The abstraction you're looking for is a memoising function. In this kind of function, you modify the getter function so that it implements an 'execute-it-only-once' kind of pattern. Untested code but roughly;

public Func<T> Memoize(Func<T> slowFunction) {
    bool evaluated = false;
    T value = default(T);

    return () => {
        if (!evaluated) {
            value = slowFunction();
            evaluated = true;
        }

        return value;
    };
}

So you now have a function which you can use like this;

Func<TConfiguration> onceOnlyLoad = Memoise(Load);

Now you can call onceOnlyLoad() as many times as you like and it'll only load the config first time you call it.

If you want, you can use Lazy<T> internally to giv the same behaviour. Up to you.

Upvotes: 1

Related Questions