Delegates have been a great help to developers everywhere; coupled with Events they form the basis of every asp.net web application.  Check out the page event life cycle, events are firing long before the code written by the sites developer.  But what happens when events and delegates are used with a return value?

I've heard people say that it depends on the return type, it varies by how many functions have been added to the delegate, etc etc...

Poppycock.  Never thought I would use that word, however I have no idea the age of everyone who visits here.

It is all about the order.  Users have dealt with this same issue for years; If two users open the same file from the network, edit and save it whose changes are kept or lost?  Last save wins.  So the last delegate to "save" the result wins.  So which Delegate is last? 

Delegates, like good fast food, follow fifo (First In First Out).  Before anyone picks on that statement and points out how many times they placed an order only to get it after the guy who order after them, I said "good fast food".  You'll find that hanging out with Santa and the Easter bunny.

The order that methods are added to the delegate is the order they will fire in.  So the last one added is the last one to run and, therefore, controls the result.

But do not take my word for it, let the code do the talking.  Here is the contents for a Program.cs file from a C# console application project.

  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;

  namespace Delegates
  {
      public delegate ReturnType LessonEventHandler<ReturnType>(object sender, EventArgs e);

      class Program
      {
          static void Main(string[] args)
          {
              int order = 1;              RunTestByOrder(order);
              order++;
              RunTestByOrder(order);
              Console.WriteLine("Press any key to exit.");
              Console.ReadLine();
            }

           static void RunTestByOrder(int order)
          {
              Console.WriteLine("Order " + order.ToString() + ":Start");
              EventMaster master = new EventMaster();
              RunBooleanTests(master, order);
              RunInt32Tests(master, order);
              Console.WriteLine("Order " + order.ToString() + ":End");
          }

          static void RunBooleanTests(EventMaster master, int order)
          {
              Console.WriteLine("BooleanTest:Start");
              switch (order)
              {
                  case 2:
                      master.BooleanTest += new LessonEventHandler<bool>(master_BooleanTest2);
                      master.BooleanTest += new LessonEventHandler<bool>(master_BooleanTest1);
                      break;
                  case 1:
                  default:
                      master.BooleanTest += new LessonEventHandler<bool>(master_BooleanTest1);
                      master.BooleanTest += new LessonEventHandler<bool>(master_BooleanTest2);
                      break;
              }
              Console.WriteLine("BooleanTest result: " + master.DoBooleanTest().ToString());
              Console.WriteLine("BooleanTest:End");
          }

          static void RunInt32Tests(EventMaster master, int order)
          {
              Console.WriteLine("Int32Test:Start");
              switch (order)
              {
                  case 2:
                      master.Int32Test += new LessonEventHandler<int>(master_Int32Test4);
                      master.Int32Test += new LessonEventHandler<int>(master_Int32Test3);
                      master.Int32Test += new LessonEventHandler<int>(master_Int32Test2);
                      master.Int32Test += new LessonEventHandler<int>(master_Int32Test1);
                      break;
                  case 1:
                  default:
                      master.Int32Test += new LessonEventHandler<int>(master_Int32Test1);
                      master.Int32Test += new LessonEventHandler<int>(master_Int32Test2);
                      master.Int32Test += new LessonEventHandler<int>(master_Int32Test3);
                      master.Int32Test += new LessonEventHandler<int>(master_Int32Test4);
                      break;
              }
              Console.WriteLine("Int32Test result: " + master.DoInt32Test().ToString());
              Console.WriteLine("Int32Test:End");
          }
            static int master_Int32Test1(object sender, EventArgs e)
          {
              Int32 i = 10;
              Console.WriteLine("Int32Test:" + i.ToString());

          return i;
          }

          static int master_Int32Test2(object sender, EventArgs e)
          {
              Int32 i = 20;
              Console.WriteLine("Int32Test:" + i.ToString());
              return i;
          }

          static int master_Int32Test3(object sender, EventArgs e)
          {
              Int32 i = 30;
              Console.WriteLine("Int32Test:" + i.ToString());
              return i;
          }
            static int master_Int32Test4(object sender, EventArgs e)
          {
              Int32 i = 40;
              Console.WriteLine("Int32Test:" + i.ToString());
              return i;
          }
          static bool master_BooleanTest1(object sender, EventArgs e)
          {
              Console.WriteLine("BooleanTest:True");
              return true;
          }
          static bool master_BooleanTest2(object sender, EventArgs e)
          {
              Console.WriteLine("BooleanTest:False");
              return false;
          }
      }
        public class EventMaster
      {
          public event LessonEventHandler<Boolean> BooleanTest;

          public event LessonEventHandler<Int32> Int32Test;            public Boolean DoBooleanTest()
          {
              return BooleanTest(this, EventArgs.Empty);
          }
            public Int32 DoInt32Test()
          {
              return Int32Test(this, EventArgs.Empty);
          }
      }  
}
  • If you need to do something like a "IsCanceled" for an event do not use the return from the delegate, use a property in a custom EventArgs object. 
  • There is no way to see the result of the method that fired before you in the delegate. 
  • If there could be more than one method wired into a delegate the result is fairly worthless.  If there is only on method wired in, how has it been secured so no one else can hook into it?  Private/protected?  why not just use a virtual method?

When is a delegate with a return useful?  Like everything, it has its place and use; for example: Threading!  They can be used to ensure thread safety.  But that is topic for another day.

Happy Coding!