A Quick Observation of C# Anonymous Methods with Delegates and Lack of Loop Unrolling

Disclaimer: C# Express/March CTP/.NET version v2.0.50110

This is for the performance concious freaks out there (like me). The following code results in a new Delegate instance being constructed every pass through the loop even in release builds:

private void MyBackgroundWorker(object state)
{
 for(int work = 0; work < 10000; work++)
 {
   this.Invoke(new MethodInvoker(
                   delegate()
                   {
                    this.label1.Text = work.ToString();
                   }));
 }
}

If you disassemble this version and have a look at the IL you’ll see the MethodInvoker is newobj‘d every pass through the loop. Curious… can’t C# just unroll this version? If not, why not? So while the short hand, inline looks great and probably the nicest to maintain, beware the potential performance costs (unecessary allocations of the delegate) as opposed to this far less elegant, yet more efficient approach:

private void MyBackgroundWorker(object state)
{
 int work = 0;

 MethodInvoker myCallback = new MethodInvoker(
                            delegate()
                            {
                              this.label1.Text = work.ToString();
                            });

 for(work = 0; work < 10000; work++)
 {
  this.Invoke(myCallback);
 }
}

I guess my next question is for all the real hardcore folks out there that like to get into the JIT’d machine code: Is it unrolled by the JIT? I guess I could just attach the profiler and see how man MethodInvoker’s are actually instantiated at runtime, but I’m only running C# Express on this box so I don’t have it and AFAIK there’s no “official” CLR Profiler for .NET 2.0 yet.

On a related note, I find it really annoying that I have to do new MethodInvoker at all. Why can’t I just do:

this.Invoke(delegate()
            {
              this.label1.Text = work.ToString();
            });

I guess in the particular case of Control::Invoke I can understand the need to know exactly what kind of delegate instance to construct because it takes Delegate which is an abstract class. However, even when the type of delegate could be inferred from the method signature it is not. :

5 thoughts on “A Quick Observation of C# Anonymous Methods with Delegates and Lack of Loop Unrolling

  1. I’m at home and don’t have a .NET 2 install available, but in general, you cannot infer delegate type based on the argument list at the caller’s site. Two delegate types with identical signature are still different.

    I just tried this and it doesn’t compile; delegate types *really* have a strong identity and the compiler won’t let you play loose with them (typecasting fails as well).

    delegate void StringCallback(string argument);
    delegate void OtherStringCallback(string argument);

    private void StringHandler(string argument) { }


    OtherStringCallback other = new OtherStringCallback(StringHandler);
    other = new StringCallback(StringHandler); // -> boom

  2. Marcelo,

    Hey, thanks for the feedback. What you say about the delegates being strongly typed is certainly true. It’s been this way since day one as you mention. What I’m saying though is that when I specifically declare an anonymous method in the place where a delegate should be passed, if the signature of the method is statically distinguishable as a certain delegate type the compiler still forces me to do the new FooDelegate(…). This seems to go against the compiler’s ability to determine the right delegate for event handlers in C# where you never need to do the new FooHandler(…).

    Like I said, I can understand the need to be explicit if I am calling a method with a signature where it is unclear as what delegate type should be inferred just based on signature (i.e. the Control::Invoke).

    Cheers,
    Drew

  3. Ah – getcha. You are correct that the compiler could go looking for delegate types that match the signature. The cases where the compiler does get it right is when the delegate type is specified in the callee’s site, such as

    private void CallMe(StringCallback callback) { }

    Which you can invoke with CallMe(delegate(string argument) { /*code*/ });

    You can still confuse the compiler (or rather, cause it to fail with an ‘ambiguous call’ error), by introducing an overload that takes a delegate of a type with compatible signature (say, an overload of CallMe with OtherStringCallback). In this case, you can always specify the delegate type directly like you do in your Invoke example, and it should work.

    Fun stuff for a Wednesday morning, which had me firing up VS to double-check what I’m saying. 🙂

  4. Marcelo,

    Yeah, sorry should have been more specific in the original post). My specific case happens to be something like the following:

    delagate SomeDelegate();
    delegate ADifferentDelegate(object state);

    Foo(SomeDelegate callback);
    Foo(ADifferentDelegate callback, object state);

    In this case, even though it’s perfectly clear if I’m calling one vs. the other due to the diff. in parameter counts, the compiler still seems to be forcing me to be explicit about my delegate type.

    At least it works for the simple single signature scenarios, but I just don’t see a reason why it couldn’t work for the scenario which I’m describing too.

    In any case, thanks again for all the feedback, I appreciate it.

    Cheers,
    Drew

Leave a Reply