Welcome to MSDN Blogs Sign in | Join | Help

The implementation of anonymous methods in C# and its consequences (part 2)

Last time we took a look at how anonymous methods are implemented. Today we'll look at a puzzle that can be solved with what we've learned. Consider the following program fragment:

using System;

class MyClass {
 delegate void DelegateA();
 delegate void DelegateB();

 static DelegateB ConvertDelegate(DelegateA d)
 {
  return (DelegateB)
   Delegate.CreateDelegate(typeof(DelegateB), d.Method);
 }

 static public void Main()
 {
  int i = 0;
  ConvertDelegate(delegate { Console.WriteLine(0); });
 }
}

The ConvertDelegate method merely converts a DelegateA to a DelegateB by creating a DelegateB with the same underlying method. Since the two delegate types use the same signature, this conversion goes off without a hitch.

But now let's make a small change to that Main function:

 static public void Main()
 {
  int i = 0;
  // one character change - 0 becomes i
  ConvertDelegate(delegate { Console.WriteLine(i); });
 }

Now the program crashes with a System.ArgumentException at the point where we try to create the DelegateB. What's going on?

First, observe that the overload of Delegate.CreateDelegate that was used is one that can only be used to create delegates from static methods. Next, note that in Test1, the anonymous method references neither its own members nor any local variables from its lexically-enclosing method. Therefore, the resulting anonymous method is a "static anonymous method of the easy type". Since the anonymous method is a static member, the use of the "static members only" overload of Delegate.CreateDelegate succeeds.

However, in Test2, the anonymous method dereferences the i variable from its lexically-enclosing method. This forces the anonymous method to be a "anonymous method of the hard type", and those anonymous methods use an anonymous instance member function of an anonymous helper class. As a result, d.Method is an instance method, and the chosen overload of Delegate.CreateDelegate throws an invalid parameter exception since it works only with static methods.

The solution is to use a different overload of Delegate.CreateDelegate, one that work with either static or instance member functions.

 DelegateB ConvertDelegate(DelegateA d)
 {
  return (DelegateB)
   Delegate.CreateDelegate(typeof(DelegateB), d.Target, d.Method);
 }

The Delegate.CreateDelegate(Type, Object, MethodInfo) overload creates a delegate for a static method if the Object parameter is null or a delegate for an instance method if the Object parameter is non-null. Hardly by coincidence, that is exactly what d.Target produces. If the original delegate is for a static method, then d.Target is null; otherwise, it is the object for which the instance method is to be invoked on.

This fix, therefore, makes the ConvertDelegate function handle conversion of delegates for either static or instance methods. Which is a good thing, because it may now be called upon to convert delegates for instance methods as well as static ones.

Okay, this time we were lucky that the hidden gotcha of anonymous methods resulted in an exception. Next time, we'll see a gotcha that merely results in incorrect behavior that will probably take you forever to track down.

Published Thursday, August 03, 2006 7:00 AM by oldnewthing
Filed under:

Comments

# re: The implementation of anonymous methods in C# and its consequences (part 2)

Thursday, August 03, 2006 10:41 AM by Carlos
It's easier to write:

DelegateB ConvertDelegate(DelegateA d)
{
 return delegate { d(); };
}

Additionally, this also works if d is multicast.  (Note that the new delegate is a deep copy: it isn't affected by changes to the original delegate's invocation list.)

The tradeoff is that it's cheaper to create the new delegate but more expensive to call it.

# Interesting Finds: August 2

Thursday, August 03, 2006 11:25 PM by Jason Haley

# Equivalence of delegates?

Sunday, August 06, 2006 12:00 PM by Grant Husbands
I know it's not the case, but shouldn't DelegateA and DelegateB be trivially the same type, anyway?   (This is an open question - I'm not meaning to imply that you, Raymond, should know everything about every .NET design decision and I hope you don't mind me posting an open question, here.)

It certainly blocked me when I wanted to create a generics-based parameter-binding library, along the lines of boost::bind.

# The implementation of anonymous methods in C# and its consequences

Tuesday, August 08, 2006 1:23 PM by Eric Gunnerson's C# Compendium
Raymond wrote a really nice series of posts on this:

Part 1
Part 2
Part 3
He also points out that...

# The implementation of anonymous methods in C# and its consequences #2

Wednesday, August 09, 2006 3:51 AM by DotNetKicks.com
You've been kicked (a good thing) - Trackback from DotNetKicks.com

# Anonymous Methods -> code bloat

Monday, August 14, 2006 11:09 AM by Anonymous Methods -> code bloat

# Tim Van Wassenhove » Blog Archive » Anonymous methods

Saturday, September 16, 2006 5:13 PM by Tim Van Wassenhove » Blog Archive » Anonymous methods

# Tim Van Wassenhove » Anonymous methods

Saturday, October 07, 2006 8:03 PM by Tim Van Wassenhove » Anonymous methods

# Julio C??sar Carrascal Urquijo » Archivo » D should have real closures.

# New for Visual Studio 2008 - Support for anonymous methods and lambda expressions

Friday, September 21, 2007 11:36 AM by The Visual Studio Code Analysis Team Blog

One of my favorite new features for Code Analysis in Visual Studio 2008 is our support for analyzing

# Weekly linkdump #39 - max - ???????? ??????????????????????????

# ReSharper: Access to modified closure

Monday, April 06, 2009 3:20 PM by Patrick Steele's .NET Blog

On the advice of Jay Wren , I decided to try our ReSharper 4.1 .  I had previously installed DevExpress

# ReSharper: Access to modified closure

Monday, April 06, 2009 3:27 PM by Patrick Steele

On the advice of Jay Wren , I decided to try our ReSharper 4.1 .  I had previously installed DevExpress'

# For once and for all: Delegates, Events, Anonymous Methods and Lambda Expressions « Hungry for Knowledge

# Anonymous Methods Internals | Beyond The Spec

Thursday, June 18, 2009 7:52 PM by Anonymous Methods Internals | Beyond The Spec
New Comments to this post are disabled
 
Page view tracker