Skip to main content

Curious Case Of Anonymous Delegates !!!

Senthil has left us thrilled in his new post, and also inspired me to write about the topic. Although, anonymous delegates have become a mundane stuff amongst programmers, there is still these subtle stuff left unexplored. Alright, let us try to answer Senthil's question before he unravels the mystery in his next post.

A delegate is identified by its target. The target is the method to be executed on a delegate invocation and its associated instance or type. If the target is an instance method, the delegate preserves the target method pointer and the object instance. If the target is a static method, the delegate preserves the target method pointer and the type to which it belongs. So when a code like the one below to register a method to an event (or multicast delegate) is executed, a delegate object (EventHandler here) with the target information embedded is created and added to the invocation list of the event (or multicast delegate, KeyPressed here).

class SomeForm
{
   private Control control = new Control();

   public void OnFormLoad(object sender, EventArgs args)
   {
      control.KeyPressed += new EventHandler(OnKeyPressed);
   }

   // Rest of the code omitted to be succinct
};

Likewise, when unregistering the method handler, a new (EventHandler) delegate object is created with the same target information as above. As said earlier, a delegate is identified by its target. In other words, the Equals override on the delegate uses the target information for comparing two delegate objects. Hence in the following code that unregisters the method handler, the invocation list is searched for a delegate instance with the specified target information (Method: OnKeyPressed, Instance: SomeForm instance).

In the case of anonymous delegates, the compiler transforms the inline method code into a

  • static method, if the inline method code does not use any of the class's instance members or local variables or if it uses only the static members of the class.
  • instance method, if the inline method code uses at least one class member, any or no static members, and no local variables.
  • class with a method that represents the inline method code, if the inline method code uses local variables no matter whether it uses the class members or not.

Those might not be the extensive set of rules but sure are enough for our discussion. Given the following questionable code,

public EventHandler IfEnabledThenDo(EventHandler actualAction)
{
   return (sender, args) => {
      if (args.Control.Enabled)
      {
         actualAction(sender, args);
      }
   };
}

public void Initialize()
{
   control.KeyPressed += IfEnabledThenDo(control_KeyPressed);
}

public void Destroy()
{
   control.KeyPressed -= IfEnabledThenDo(control_KeyPressed);
}

we realize, without doubt, that the anonymous delegate (returned by IfEnabledThenDo) would be transported into a compiler generated anonymous class. Later when IfEnabledThenDo is called for registering\unregistering the method handler, an instance of anonymous class is created and the (EventHandler) delegate is returned. And here lies the subtlety. Although the delegate from IfEnabledThenDo targets the method inside the anonymous class, the instance preserved as a part of the target information are different during registration and un-registration. In other words, the target method of the delegate returned by IfEnabledThenDo belong to different instances of the anonymous class. Hence the pretty code to unregister the (key pressed) method handler would not be actually unregistering since there would be a delegate previously registered in the invocation list of the (KeyPressed) event with the target instance same as the one used in the unregistration line of code. Very subtle!

Usually the hand written code tends to keep the registration and unregistration of the method handlers in the same class and so belong to the respective instances. Not so when you like watching the compiler magic.

Let us wait and see what Senthil says.

Comments

Popular posts from this blog

Extension Methods - A Polished C++ Feature !!!

Extension Method is an excellent feature in C# 3.0. It is a mechanism by which new methods can be exposed from an existing type (interface or class) without directly adding the method to the type. Why do we need extension methods anyway ? Ok, that is the big story of lamba and LINQ. But from a conceptual standpoint, the extension methods establish a mechanism to extend the public interface of a type. The compiler is smart enough to make the method a part of the public interface of the type. Yeah, that is what it does, and the intellisense is very cool in making us believe that. It is cleaner and easier (for the library developers and for us programmers even) to add extra functionality (methods) not provided in the type. That is the intent. And we know that was exercised extravagantly in LINQ. The IEnumerable was extended with a whole lot set of methods to aid the LINQ design. Remember the Where, Select etc methods on IEnumerable. An example code snippet is worth a thousand ...

Implementing COM OutOfProc Servers in C# .NET !!!

Had to implement our COM OOP Server project in .NET, and I found this solution from the internet after a great deal of search, but unfortunately the whole idea was ruled out, and we wrapped it as a .NET assembly. This is worth knowing. Step 1: Implement IClassFactory in a class in .NET. Use the following definition for IClassFactory. namespace COM { static class Guids { public const string IClassFactory = "00000001-0000-0000-C000-000000000046"; public const string IUnknown = "00000000-0000-0000-C000-000000000046"; } /// /// IClassFactory declaration /// [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid(COM.Guids.IClassFactory)] internal interface IClassFactory { [PreserveSig] int CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject); [PreserveSig] int LockServer(bool fLock); } } Step 2: [DllImport("ole32.dll")] private static extern int CoR...

Passing CComPtr By Value !!!

This is about a killer bug identified by our chief software engineer in our software. What was devised for ease of use and write smart code ended up in this killer defect due to improper perception. Ok, let us go! CComPtr is a template class in ATL designed to wrap the discrete functionality of COM object management - AddRef and Release. Technically it is a smart pointer for a COM object. void SomeMethod() { CComPtr siPtr; HRESULT hr = siPtr.CoCreateInstance(CLSID_SomeComponent); siPtr->MethodOne(20, L"Hello"); } Without CComPtr, the code wouldn't be as elegant as above. The code would be spilled with AddRef and Release. Besides, writing code to Release after use under any circumstance is either hard or ugly. CComPtr automatically takes care of releasing in its destructor just like std::auto_ptr . As a C++ programmer, we must be able to appreciate the inevitability of the destructor and its immense use in writing smart code. However there is a difference...