views:

92

answers:

2

This was mentioned in my other question and I thought it might be useful to add it to the record. In the following program, which, if any, of the locally defined delegates are cached between calls to the Work method instead of being created from scratch each time?

namespace Example
{
    class Dummy
    {
        public int age;
    }

    class Program
    {
        private int field = 10;

        static void Main(string[] args)
        {
            var p = new Program();

            while (true)
            {
                p.Work();
            }
        }

        void Work()
        {
            int local = 20;

            Action a1 = () => Console.WriteLine(field);
            Action a2 = () => Console.WriteLine(local);
            Action a3 = () => Console.WriteLine(this.ToString());
            Action a4 = () => Console.WriteLine(default(int));
            Func<Dummy, Dummy, bool> dummyAgeMatch = (l, r) => l.age == r.age;

            a1.Invoke();
            a2.Invoke();
            a3.Invoke();
            a4.Invoke();
            dummyAgeMatch.Invoke(new Dummy() { age = 1 }, new Dummy(){ age = 2 });
        }
    }
}
+4  A: 

Based on what Reflector shows, the last two (a4 and dummyAgeMatch) are cached by the MS C#3.0 compiler (in fields called "CS$<>9_CachedAnonymousMethodDelegate5" and "CS$<>9_CachedAnonymousMethodDelegate6" in my particular build).

These ones can be cached, whereas obviously the others depend on captured variables.

I don't believe the behaviour is mandated by the spec, but the Mono compiler behaves in much the same way (with different variable names).

Jon Skeet
+2  A: 

Well, to answer the specific question: the last two are the only ones that are cached, since they don't capture anything. You can see this in reflector (but it isn't pretty). Of course, you can tweak them to make them re-usable by passing in args:

Action<Program> a1 = p => Console.WriteLine(p.field);
Action<int> a2 = i => Console.WriteLine(i);
Action<Program> a3 = p => Console.WriteLine(p.ToString());
Action a4 = () => Console.WriteLine(default(int));
Func<Dummy, Dummy, bool> dummyAgeMatch = (l, r) => l.age == r.age;

And pass this into a1/a3, and local into a2. Because none of them directly capture anything any more, they can all be cached (CS$<>9__CachedAnonymousMethodDelegate5 thru CS$<>9__CachedAnonymousMethodDelegate9 for me, at least). Of course, they then lose the ability to re-assign the (previously captured) variable directly, but FP advocates won't like that anyway ;-p You can always pass the updated value back as a return value, or declare a delegate type with ref arguments (I don't recommend it, though).

Marc Gravell
Thank you - that's novel!
frou