.NET Development Foundation/Using events


System types and collections: Using events and delegates


Using events and delegates

edit

Exam objectives: Control interactions between .NET Framework application components by using events and delegates.

(Refer System namespace)

Delegate class - MSDN

Delegates hold pointers to one or more functions and invoke them as needed.
One common use of delegates is for event handling. A class that raises an event does not know what objects or methods want to receive the event, so an intermediary or pointer mechanism is needed between the object raising the event and the object(s) receiving the event. Delegates can be used as function pointers to accomplish this.
A delegate is a class, but unlike a regular class it has a signature. In the .Net framework you just declare the delegate and the CLR handles the implementation of the class.
   //delegate declaration
   public delegate void AlarmEventHandler(object sender,EventArgs e);
The first complete example just declare a delegate type, then declare a variable of that type, assign a function to it and execute the delegate variable which has the effect of executing the function.
C# sample

Simple delegate

   using System;
   using System.Collections.Generic;
   using System.Text;
   //
   namespace DelegateLab01
   {
       // declare the delegate type
       public delegate int IntOperDel(int i);
       //
       class Program
       {
           static void Main(string[] args)
           {
               Program pgm = new Program();
               // Assign the delegate
               IntOperDel deleg = pgm.Increment;
               // Executing the delegate
               int res = deleg(32);
               Console.WriteLine("First value: " + res.ToString());
               // Second assign
               deleg = pgm.Decrement;
               // Second execution
               res = deleg(32);
               Console.WriteLine("First value: " + res.ToString());
               // Wait for finish
               Console.WriteLine("Press ENTER to finish");
               Console.ReadLine();
           }
           // First function to be assigned to the delegate
           public int Increment(int n)
           {
               return n + 1;
           }
           // Second function to be assigned to the delegate
           public int Decrement(int n)
           {
               return n - 1;
           }
       }
   }
The second delegate example implement the concept of a callback function. A function is called with a delegate as an argument. When it executes the delegate it has no knowledge of exactly what function is executed. Part of its behavior is delegated to the function passed as a parameter (thru the delegate).
C# sample

Callback delegate

   using System;
   using System.Collections.Generic;
   using System.Text;
   //
   namespace DelegateLab02
   {
       // declare the delegate type
       public delegate int IntOperDel(int i);
       //
       class Program
       {
           static void Main(string[] args)
           {
               Program pgm = new Program();
               // Assign the delegate
               IntOperDel deleg = pgm.Increment;
               // Calling a function that will execute de delegate
               // as part of its own logic
               pgm.ExecuteCallBack(deleg);
               // Wait for finish
               Console.WriteLine("Press ENTER to finish");
               Console.ReadLine();
           }
           // Function to be assigned to a delegate
           public int Increment(int n)
           {
               return n + 1;
           }
           // Function called with a delegate as parameter
           public void ExecuteCallBack(IntOperDel deleg)
           {
               int res = deleg(32);
               Console.WriteLine("Result from executing the callback: " + res.ToString());
           }
       }
   }
The third delegate example uses a delegate member to produce the same pattern as an event. Note that executing the delegate member without assigning at least one function will cause an exception. Also assigning 2 functions with the += operator will execute the 2 functions when the delegate is executed. If the delegate has a return code, the return value of the last executed function will be returned.
C# sample

Delegate member

   using System;
   using System.Collections.Generic;
   using System.Text;
   //
   namespace DelegateLab03
   {
       // declare the delegate type
       public delegate void IntOperDel(int i);
       //
       class Program
       {
           static void Main(string[] args)
           {
               Program pgm = new Program();
               // Use += oper to assign functions to the delegate
               pgm.delMember += pgm.Increment;
               pgm.delMember += pgm.Decrement;
               // Calling some member function that will execute the delegate
               pgm.ExecuteSomething();
               // Wait for finish
               Console.WriteLine("Press ENTER to finish");
               Console.ReadLine();
           }
           // First function to be assigned to a delegate
           public void Increment(int n)
           {
               int res = n + 1;
               Console.WriteLine("Inside increment function: " + res.ToString());
           }
           // Second function to be assigned to a delegate
           public void Decrement(int n)
           {
               int res = n - 1;
               Console.WriteLine("Inside decrement function: " + res.ToString());
           }
           // Class member of the delegate type
           public IntOperDel delMember;
           // Function called to execute the delegate member (raise the "event")
           public void ExecuteSomething()
           {
               this.delMember(32);
           }
       }
   }
The fourth example is the basic example of an event. It is exactly as the third example with the event keyword added to the member declaration and a test before calling the event because of the exception thrown on an "empty" event. This example shows clearly that an event is nothing more then a multicast delegate.
C# sample

Event member

   using System;
   using System.Collections.Generic;
   using System.Text;
   //
   namespace DelegateLab04
   {
       // declare the delegate type
       public delegate void IntOperDel(int i);
       //
       class Program
       {
           static void Main(string[] args)
           {
               Program pgm = new Program();
               // Use += oper to assign functions to the delegate
               pgm.delMember += pgm.Increment;
               pgm.delMember += pgm.Decrement;
               // Calling some member function that will execute the delegate
               pgm.ExecuteSomething();
               // Wait for finish
               Console.WriteLine("Press ENTER to finish");
               Console.ReadLine();
           }
           // First function to be assigned to a delegate
           public void Increment(int n)
           {
               int res = n + 1;
               Console.WriteLine("Inside increment function: " + res.ToString());
           }
           // Second function to be assigned to a delegate
           public void Decrement(int n)
           {
               int res = n - 1;
               Console.WriteLine("Inside decrement function: " + res.ToString());
           }
           // Class member event of the delegate type
           public event IntOperDel delMember;
           // Function called to execute the delegate member (raise the "event")
           public void ExecuteSomething()
           {
               if (this.delMember != null)
                   this.delMember(32);
           }
       }
   }
Executing the functions associated with an event is called raising the event.
The functions associated with the event are called event handlers

EventArgs class - MSDN

By convention an event uses a delegate that returns void and take 2 arguments:
  • an object of type System.Object that contains a reference to the object that raised the event.
  • an object from a class derived from EventArgs that contains the data passed from the object that raised the event to the event handlers.
The EventArgs class does not contain any data by itself.
So an event with no data will use a delegate of the form
public delegate void DelegateTypeName (object sender, EventArgs e)
This simple event example is the same as the last one with the IntEventArgs class that serves for passing the int argument to the event handler and all the parameters changed to follow the calling convention for events.
C# sample

Event with delegate that follows the calling convention

   using System;
   using System.Collections.Generic;
   using System.Text;
   //
   namespace EventLab01
   {
       // the class containing the event data passed to the event handler
       public class IntEventData : EventArgs
       {
           public int IntParm = 0;
           // constructor
           public IntEventData(int i)
           {
               IntParm = i;
           }
       }
       // the delegate type
       public delegate void IntOperDel(object sender, IntEventData e);
       //
       class Program
       {
           static void Main(string[] args)
           {
               Program pgm = new Program();
               // Use += oper to assign functions to the delegate
               pgm.delMember += pgm.Increment;
               pgm.delMember += pgm.Decrement;
               // Calling some member function that will execute the delegate
               pgm.ExecuteSomething();
               // Wait for finish
               Console.WriteLine("Press ENTER to finish");
               Console.ReadLine();
           }
           // First function to be assigned to a delegate
           public void Increment(object sender, IntEventData e)
           {
               int res = e.IntParm + 1;
               Console.WriteLine("Inside increment function: " + res.ToString());
           }
           // Second function to be assigned to a delegate
           public void Decrement(object sender, IntEventData e)
           {
               int res = e.IntParm - 1;
               Console.WriteLine("Inside decrement function: " + res.ToString());
           }
           // Class member event of the delegate type
           public event IntOperDel delMember;
           // Function called to execute the delegate member (raise the "event")
           public void ExecuteSomething()
           {
               if (this.delMember != null)
                   this.delMember(this, new IntEventData(32));
           }
       }
   }

EventHandler delegates - MSDN and MSDN

There are two special delegates defined in the System namespace to help you with the events declarations.
The first is the EventHandler delegate. It passes no data. An event that passes no data can be declared as:
public event EventHandler EventName
without having to declare a custom delegate.
This is the usual way to declare an event that passes no data.
C# sample

Event with EventHandler delegate

   using System;
   using System.Collections.Generic;
   using System.Text;
   //
   namespace EventLab03
   {
       //
       class Program
       {
           static void Main(string[] args)
           {
               Program pgm = new Program();
               // Use += oper to assign functions to the delegate
               pgm.delMember += pgm.Increment;
               pgm.delMember += pgm.Decrement;
               // Calling some member function that will execute the delegate
               pgm.ExecuteSomething();
               // Wait for finish
               Console.WriteLine("Press ENTER to finish");
               Console.ReadLine();
           }
           // First function to be assigned to the event
           public void Increment(object sender, EventArgs e)
           {
               int res = 32 + 1;
               Console.WriteLine("Inside increment function: " + res.ToString());
           }
           // Second function to be assigned to the event
           public void Decrement(object sender, EventArgs e)
           {
               int res = 32 - 1;
               Console.WriteLine("Inside decrement function: " + res.ToString());
           }
           // Class member event of the delegate type
           public event EventHandler delMember;
           // Function called to execute the delegate member (raise the "event")
           public void ExecuteSomething()
           {
               if (this.delMember != null)
                   this.delMember(this, null);
           }
       }
   }
The second is the EventHandler<T> generic delegate where T is a type derived from EventArgs
public event EventHandler<T> EventName
again no need to declare a custom delegate type.
This is the usual way to declare an event that passes data to the event handlers.
Note that in this case you still have to declare the type T derived from EventArgs.
C# sample

Event utilizing EventHandler<T>

   using System;
   using System.Collections.Generic;
   using System.Text;
   //
   namespace EventLab02
   {
       // the class containing the event data passed to the event handler
       public class IntEventData : EventArgs
       {
           public int IntParm = 0;
           // constructor
           public IntEventData(int i)
           {
               IntParm = i;
           }
       }
       //
       class Program
       {
           static void Main(string[] args)
           {
               Program pgm = new Program();
               // Use += oper to assign functions to the delegate
               pgm.delMember += pgm.Increment;
               pgm.delMember += pgm.Decrement;
               // Calling some member function that will execute the delegate
               pgm.ExecuteSomething();
               // Wait for finish
               Console.WriteLine("Press ENTER to finish");
               Console.ReadLine();
           }
           // First function to be assigned to a delegate
           public void Increment(object sender, IntEventData e)
           {
               int res = e.IntParm + 1;
               Console.WriteLine("Inside increment function: " + res.ToString());
           }
           // Second function to be assigned to a delegate
           public void Decrement(object sender, IntEventData e)
           {
               int res = e.IntParm - 1;
               Console.WriteLine("Inside decrement function: " + res.ToString());
           }
           // Class member event of the delegate type
           public event EventHandler<IntEventData> delMember;
           // Function called to execute the delegate member (raise the "event")
           public void ExecuteSomething()
           {
               if (this.delMember != null)
                   this.delMember(this, new IntEventData(32));
           }
       }
   }
This concludes the basic examples for events and delegates.