Deferred execution in C#
Asked Answered
C

6

15

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?

Cronyism answered 10/9, 2009 at 1:8 Comment(0)
P
19

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);
Pauper answered 10/9, 2009 at 1:10 Comment(9)
But how is "string x = doit()" any different from "string x = DoFoo()", apart from the extra layer of indirection?Bunnybunow
This still calls 'DoFoo()' before x is "used".Busman
After re-reading the question, what about this? (I cannot test it right now, though.)Pauper
I've tested it here, and it's slightly wrong (the nullable), after making some slight modifications, I ended up with a StackOverflow (but solvable I think if you tried). Even if it did work, I'm not a fan because it's so confusing, but it's a cute try :)Busman
Yes, the implicit operators are a usability nightmare. I'd favor a more verbose approach. But this is the closest he can get, I think. @silky, feel free to edit my answer to fix any bugs.Pauper
dtb: I'll only edit it if I can get it to work. StackOverflow solved now it's just resulting in 'y' not being assigned anything. I'll give up for now, work to do :) [if (lazy == null ){ lazy.value = lazy.func(); } return lazy.value;] and removal of nullable.Busman
@dtb, @silky: I edited to make Lazy<T> work correctly. Unfortunately the compiler's type inferencing isn't strong enough to allow the "Lazy<string> x = DoFoo" syntax, so although everything works properly the assignment statements are a bit clunky.Bunnybunow
FYI, the next version of the framework will have a beautiful threadsafe Lazy helper class. Until then, you can get the source code for it here: bluebytesoftware.com/blog/2007/06/09/…Rosalynrosalynd
this is why javascript will rule the known world.Pfeifer
S
6

One option is to use the Lazy<T> class, formerly from the parallel extensions library now a part of the .Net Framework 4.0.

It allows you to delay process data in a thread aware manner.

Stichous answered 10/9, 2009 at 2:5 Comment(1)
I much prefer using a system class to do so. However, the system class doesn't implement implicit operator, which prompted me to search why. I found this, which kind of explains why.Longhair
J
5

While it's somewhat dirty you could always use the yield keyword:

public IEnumerable<int> DoFoo() {
   Console.WriteLine("doing foo");
   yield return 10;
}

[Test]
public void TestMethod()
{
    var x = DoFoo();
    Console.WriteLine("foo aquired?");
    Console.WriteLine(x.First());
}
Johny answered 10/9, 2009 at 1:33 Comment(0)
H
1

Instead of passing a string x, pass a delegate that procures you a string

Func<String> fooFunc=()=>DoFoo();
Hyracoid answered 10/9, 2009 at 1:10 Comment(0)
B
0

Why not just not call 'DoFoo()' until you want to?

-- Edit

I mean, what do you mean "use"

For example, if you want it to be called when '.ToString()' called, you can always inherit the class and implement your function there (but this would be quite unintuitive IMHO).

Busman answered 10/9, 2009 at 1:10 Comment(0)
C
0

You pretty much describe LINQ in action. A linq query describes how to obtain the data, but data is retrieved (DoFunc is called) only when the query is iterated. Consider if you can change your design to accept IQueryable<string> where you need a string.

Celadon answered 10/9, 2009 at 1:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.