Tuesday, June 1, 2010

Lambdas as EventHandlers

At the moment I’m writing a guide to do design review for .NET 4.0 applications writen in C#. One of the small questions I had was what to do with event handlers that use lambda expressions and closures.

The problem of closures is that by using members that are on the scope of the object defining the lambda they create references to those members and can cause unexpected leaks.

One of the things I wanted to try is to see if using the same lambda (Test1) to unsubscribe would remove it from the invocation list of the event. The answer is NO it does not. I have seen code that uses this but as this test shows it does nothing. The only way to remove it from the invocation list is to hold the reference to the lamdba in a variable and use that to subscribe and unsubscribe (Test2). The code loses the beauty of using the lamdba to shorten it and I am considering to allow only the use of lamdbas that do not use members of the object.

 

class Program
{
   static void Main(string[] args)
   {
       Console.WriteLine("Test 1 : Do not hold the lambda in a variable:");
       Console.WriteLine();
       Test1();

       Console.WriteLine("Test 2 : Hold the lambda in a variable.");
       Console.WriteLine();
       Test2();
   }

   private static void Test1()
   {
       EventPublisher ep = new EventPublisher();
       ep.TestEvent += (o, e) => { };
       Console.WriteLine("Number of subscribers = {0}", ep.NumberOfSubscribers);
       ep.TestEvent -= (o, e) => { };
       Console.WriteLine("Number of subscribers = {0}", ep.NumberOfSubscribers);
       Console.ReadLine();
   }

   private static void Test2()
   {
       EventPublisher ep = new EventPublisher();

       EventHandler<EventArgs> t = (o, e) => { };

       ep.TestEvent += t;
       Console.WriteLine("Number of subscribers = {0}", ep.NumberOfSubscribers);
       ep.TestEvent -= t;
       Console.WriteLine("Number of subscribers = {0}", ep.NumberOfSubscribers);
       Console.ReadLine();
   }
}

class EventPublisher
{
   public event EventHandler<EventArgs> TestEvent;

   public int NumberOfSubscribers
   {
       get
       {
           if (this.TestEvent != null)
           {
               return this.TestEvent.GetInvocationList().Length;
           }

           return 0;
       }
   }

   public void Publish()
   {
       this.OnTestEvent(new EventArgs());
   }

   protected virtual void OnTestEvent(EventArgs e)
   {
       if (this.TestEvent != null)
       {
           this.TestEvent(this, e);
       }
   }
}

The output of this small program is the following:

Test 1 : Do not hold the lambda in a variable:

Number of subscribers = 1
Number of subscribers = 1

Test 2 : Hold the lambda in a variable.

Number of subscribers = 1
Number of subscribers = 0

As you can see beware of lambdas in Event Handlers.

Have fun,

No comments: