Search code examples
c#deferred-execution

Deferred execution in C#


How could I implement my own deferred execution mechanism in C#?

So for instance I have:

string x = DoFoo();

Is it possible to perform some magic so that DoFoo does not execute until I "use" x?


Solution

  • You can use lambdas/delegates:

    Func<string> doit = () => DoFoo();
    //  - or -
    Func<string> doit = DoFoo;
    

    Later you can invoke doit just like a method:

    string x = doit();
    

    I think the closest you can get is something like this:

    Lazy<string> x = DoFoo;
    
    string y = x; // "use" x
    

    With a definition of Lazy<T> similar to this (untested):

    public class Lazy<T>
    {
        private readonly Func<T> func;
        private bool hasValue;
        private T value;
    
        public Lazy(Func<T> func)
        {
            this.func = func;
            this.hasValue = false;
        }
    
        public static implicit operator Lazy<T>(Func<T> func)
        {
            return new Lazy<T>(func);
        }
    
        public static implicit operator T(Lazy<T> lazy)
        {
            if (!lazy.hasValue)
            {
                lazy.value = lazy.func();
                lazy.hasValue = true;
            }
            return lazy.value;
        }
    }
    

    Unfortunately, it seems that the compiler's type inferencing algorithms can't auto-infer the type of the Func<T> and so can't match it to the implicit conversion operator. We need to explicitly declare the delegate's type, which makes the assignment statements more verbose:

    // none of these will compile...
    Lazy<string> x = DoFoo;
    Lazy<string> y = () => DoFoo();
    Lazy<string> z = delegate() { return DoFoo(); };
    
    // these all work...
    Lazy<string> a = (Func<string>)DoFoo;
    Lazy<string> b = (Func<string>)(() => DoFoo());
    Lazy<string> c = new Func<string>(DoFoo);
    Lazy<string> d = new Func<string>(() => DoFoo());
    Lazy<string> e = new Lazy<string>(DoFoo);
    Lazy<string> f = new Lazy<string>(() => DoFoo);