Delegates & Events

DELEGATES:

A delegate is a reference type that can be used to encapsulate a named or an anonymous method. Delegates are similar to function pointers in C++; however, delegates are type-safe and secure. Delegate types are implicitly sealed. Delegates are the basis for Events. Delegates are object-oriented. A delegate can be passed like any other variable. This allows the method to be called anonymously, without calling the method directly

A delegate will allow us to specify what the function we’ll be calling looks like without having to specify which function to call. The declaration for a delegate looks just like the declaration for a function, except that in this case, we’re declaring the signature of functions that this delegate can reference.

There are three steps in defining and using delegates:

1. Declaring a delegate : The declaration of a delegate type is similar to a method signature. It has a return value and any number of parameters of any type:

// Declare delegate — defines required signature:
delegate double MathAction(double num);

This declaration defines a delegate named MathAction, which will encapsulate any method that takes one double as parameter and returns a double.

2. Instantiating/creating a delegate : Once a delegate type has been declared, a delegate object must be created and associated with a particular method. Like all other objects, a new delegate object is created with a new expression. When creating a delegate, however, the argument passed to the new expression is special — it is written like a method call, but without the arguments to the method.

// Instantiate delegate with named method:
MathAction madelegate = Double;

3. Calling a delegate : Once a delegate object is created, the delegate object is typically passed to other code that will call the delegate. A delegate object is called by using the name of the delegate object, followed by the parenthesized arguments to be passed to the delegate.

// Invoke delegate madelegate:
double multByTwo = madelegate(4.5);

A delegate declaration defines a type that encapsulates a method with a particular set of arguments and return type. For static methods, a delegate object encapsulates the method to be called. For instance methods, a delegate object encapsulates both an instance and a method on the instance. If you have a delegate object and an appropriate set of arguments, you can invoke the delegate with the arguments.

A delegate can be instantiated by associating it either with a named or anonymous method or lambda expression that has a compatible return type and input parameters.

The following sample demonstrates the concepts explained above:

Code Snippet
  1. // Declare delegate — defines required signature:
  2. delegate double MathAction(double num);
  3. class DelegateTest
  4. {
  5.     // Regular method that matches signature:    
  6.     static double Double(double input)
  7.     { return input * 2; }
  8.  
  9.     static void Main()
  10.     {
  11.         // Instantiate delegate with named method:     
  12.         MathAction ma = Double;
  13.         // Invoke delegate ma:       
  14.         double multByTwo = ma(4.5);
  15.         Console.WriteLine(multByTwo);
  16.         // Instantiate delegate with anonymous method:  
  17.         MathAction ma2 = delegate(double input)
  18.         { return input * input; };
  19.  
  20.         double square = ma2(5);
  21.         Console.WriteLine(square);
  22.         // Instantiate delegate with lambda expression
  23.         MathAction ma3 = s => s * s * s;
  24.         double cube = ma3(4.375); Console.WriteLine(cube);
  25.     }
  26. }

 
Using a delegate allows the programmer to encapsulate a reference to a method inside a delegate object. The delegate object can then be passed to code which can call the referenced method, without having to know at compile time which method will be invoked.

Another Sample of delegate showing how loose coupling can be achieved using delegates.

Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.IO;
  6.  
  7. namespace KishoreDelegate
  8. {
  9.     public class A1
  10.     {
  11.         // Declare a delegate that takes a single string parameter
  12.         // and has no return type.
  13.         public delegate void ADelegate(string str);
  14.  
  15.         public void F1(ADelegate adelobj)
  16.         {
  17.             adelobj("Calling function ClassB::F2() from Class A1");
  18.  
  19.         }
  20.  
  21.     }
  22.  
  23.     public class B
  24.     {
  25.         private static void F2(string str)
  26.         {
  27.             Console.WriteLine(str);
  28.         }
  29.  
  30.         static void Main(string[] args)
  31.         {
  32.             A1.ADelegate delobj = new A1.ADelegate(F2);
  33.             A1 ojba1 = new A1();
  34.             ojba1.F1(delobj);
  35.  
  36.         }
  37.     }
  38. }

 

Delegate Composition:

 

Code Snippet
  1. using System;
  2.  
  3. delegate void MyDelegate(string s);
  4.  
  5. class MyClass
  6. {
  7.     public static void Hello(string s)
  8.     {
  9.         Console.WriteLine("  Hello, {0}!", s);
  10.     }
  11.  
  12.     public static void Goodbye(string s)
  13.     {
  14.         Console.WriteLine("  Goodbye, {0}!", s);
  15.     }
  16.  
  17.     public static void Main()
  18.     {
  19.         MyDelegate a, b, c, d;
  20.  
  21.         // Create the delegate object a that references
  22.         // the method Hello:
  23.         a = new MyDelegate(Hello);
  24.         // Create the delegate object b that references
  25.         // the method Goodbye:
  26.         b = new MyDelegate(Goodbye);
  27.         // The two delegates, a and b, are composed to form c,
  28.         // which calls both methods in order:
  29.         c = a + b;
  30.         // Remove a from the composed delegate, leaving d,
  31.         // which calls only the method Goodbye:
  32.         d = c – a;
  33.  
  34.         Console.WriteLine("Invoking delegate a:");
  35.         a("A");
  36.         Console.WriteLine("Invoking delegate b:");
  37.         b("B");
  38.         Console.WriteLine("Invoking delegate c:");
  39.         c("C");
  40.         Console.WriteLine("Invoking delegate d:");
  41.         d("D");
  42.     }
  43. }

Delegates and Events:

Delegates are ideally suited for use as events — notifications from one component to "listeners" about changes in that component.

Delegates vs. Interfaces:

Delegates and interfaces are similar in that they enable the separation of specification and implementation. Multiple independent authors can produce implementations that are compatible with an interface specification. Similarly, a delegate specifies the signature of a method, and authors can write methods that are compatible with the delegate specification. When should you use interfaces, and when should you use delegates?

Delegates are useful when:

• A single method is being called.

• A class may want to have multiple implementations of the method specification.

• It is desirable to allow using a static method to implement the specification.

• An event-like design pattern is desired (for more information, see the Events Tutorial).

• The caller has no need to know or obtain the object that the method is defined on.

• The provider of the implementation wants to "hand out" the implementation of the specification to only a few select components.

• Easy composition is desired.

Interfaces are useful when:

• The specification defines a set of related methods that will be called.

• A class typically implements the specification only once.

• The caller of the interface wants to cast to or from the interface type to obtain other interfaces or classes.

Some Key Points:

  1. Delegates can be declared either outside a class definition or as part of a class through the use of the delegate keyword.

  2. Delegates have two parts : the delegate declaration and the delegate instance or static method.

  3. If an exception is thrown, the delegate stops processing methods in the invocation list. It does not matter whether or not an exception handler is present.

  4. The keyword delegate and the .NET infrastructure provided by the System.Delegate (all delegate types are derived) and System.Delegate.MulticastDelegate classes.

  5. Delegates are the base of event handling in .NET.

  6. Delegate types are implicitly sealed.

 

EVENTS:

To understand events better, we need to examine the Observer Design pattern which is used in the design of events in .NET. In the Model/View/Controller (MVC) design strategy, Model is subject and View is observer.

Observer Pattern:Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Use the observer pattern in any of the following situations:

  • When the abstraction has two aspects with one dependent on the other. Encapsulating these aspects in separate objects will increase the chance to reuse them independently.
  • When the subject object doesn’t know exactly how many observer objects it has.
  • When the subject object should be able to notify it’s observer objects without knowing who these objects are.
  • Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
  • Encapsulate the core (or common or engine) components in a Subject abstraction, and the variable (or optional or user interface) components in an Observer hierarchy as shown below.

observer-motivation

Observer

Subject represents the core (or independent or common or engine) abstraction. Observer represents the variable (or dependent or optional or user interface) abstraction. The Subject prompts the Observer objects to do their thing. Each Observer can call back to the Subject as needed.

Define an object that is the “keeper” of the data model and/or business logic (the Subject). Delegate all “view” functionality to decoupled and distinct Observer objects. Observers register themselves with the Subject as they are created. Whenever the Subject changes, it broadcasts to all registered Observers that it has changed, and each Observer queries the Subject for that subset of the Subject’s state that it is responsible for monitoring.

This allows the number and “type” of “view” objects to be configured dynamically, instead of being statically specified at compile-time.

The protocol described above specifies a “pull” interaction model. Instead of the Subject “pushing” what has changed to all Observers, each Observer is responsible for “pulling” its particular “window of interest” from the Subject.

Benefit and Drawback of Observe pattern include:

  • Abstract coupling between subject and observer, each can be extended and reused individually.
  • Dynamic relationship between subject and observer, such relationship can be established at run time. This gives a lot more programming flexibility.
  • Support for broadcast communication. The notification is broadcast automatically to all interested objects that subscribed to it.
  • Unexpected updates. Observes have no knowledge of each other and blind to the cost of changing in subject. With the dynamic relationship between subject and observers, the update dependency can be hard to track down.

The following diagram and code demonstrates the Observer pattern in which registered objects are notified of and updated with a state change.

structure of participants:

observer-structure

Subject :

knows its observers. Any number of Observer objects may observe a subject.

provides an interface for attaching and detaching Observer objects.

Observer :

defines an updating interface for objects that should be notified of changes in a subject.

ConcreteSubject :

stores state of interest to ConcreteObserver objects.

sends a notification to its observers when its state changes.

ConcreteObserver :

maintains a reference to a ConcreteSubject object.

stores state that should stay consistent with the subject’s.

implements the Observer updating interface to keep its state consistent with the subject’s.

Code Snippet
  1. class MainApp
  2. {
  3.     static void Main()
  4.     {
  5.         // Configure Observer pattern
  6.         ConcreteSubject s = new ConcreteSubject();
  7.  
  8.         s.Attach(new ConcreteObserver(s, "X"));
  9.         s.Attach(new ConcreteObserver(s, "Y"));
  10.         s.Attach(new ConcreteObserver(s, "Z"));
  11.  
  12.         // Change subject and notify observers
  13.         s.SubjectState = "ABC";
  14.         s.Notify();
  15.  
  16.         // Wait for user
  17.         Console.Read();
  18.     }
  19. }
  20.  
  21. // "Subject"
  22. abstract class Subject
  23. {
  24.     private ArrayList observers = new ArrayList();
  25.  
  26.     public void Attach(Observer observer)
  27.     {
  28.         observers.Add(observer);
  29.     }
  30.  
  31.     public void Detach(Observer observer)
  32.     {
  33.         observers.Remove(observer);
  34.     }
  35.  
  36.     public void Notify()
  37.     {
  38.         foreach (Observer o in observers)
  39.         {
  40.             o.Update();
  41.         }
  42.     }
  43. }
  44.  
  45. // "ConcreteSubject"
  46. class ConcreteSubject : Subject
  47. {
  48.     private string subjectState;
  49.  
  50.     // Property
  51.     public string SubjectState
  52.     {
  53.         get { return subjectState; }
  54.         set { subjectState = value; }
  55.     }
  56. }
  57.  
  58. // "Observer"
  59. abstract class Observer
  60. {
  61.     public abstract void Update();
  62. }
  63.  
  64. // "ConcreteObserver"
  65. class ConcreteObserver : Observer
  66. {
  67.     private string name;
  68.     private string observerState;
  69.     private ConcreteSubject subject;
  70.  
  71.     // Constructor
  72.     public ConcreteObserver(
  73.       ConcreteSubject subject, string name)
  74.     {
  75.         this.subject = subject;
  76.         this.name = name;
  77.     }
  78.  
  79.     public override void Update()
  80.     {
  81.         observerState = subject.SubjectState;
  82.         Console.WriteLine("Observer {0}'s new state is {1}",
  83.           name, observerState);
  84.     }
  85.  
  86.     // Property
  87.     public ConcreteSubject Subject
  88.     {
  89.         get { return subject; }
  90.         set { subject = value; }
  91.     }
  92. }

Events & Delegates in .NET

  • Event Handlers in the .NET Framework return void and take two parameters.
  • The first parameter is the source of the event; that is the publishing object.
  • The second parameter is an object derived from EventArgs.
  • Events are properties of the class publishing the event.
  • The keyword event controls how the event property is accessed by the subscribing classes.

An event handler in C# is a delegate with a special signature, given below.

public delegate void MyEventHandler(object sender, MyEventArgs e);

The first parameter (sender) in the above declaration specifies the object that fired the event. The second parameter (e) of the above declaration holds data that can be used in the event handler. The class MyEventArgs is derived from the class EventArgs. EventArgs is the base class of more specialized classes, like MouseEventArgs, ListChangedEventArgs, etc. For GUI event, you can use objects of these specialized EventArgs classes without creating your own specialized EventArgs classes.

In case of event handler, the delegate object is referenced using the key word event as follows

public event MyEventHandler MyEvent;
 

Now, we will set up two classes to see how this event handling mechanism works in .Net framework. The step 2 in the discussion of delegates requires that we define methods with the exact same signature as that of the delegate declaration. In our example, class A will provide event handlers (methods with the same signature as that of the delegate declaration). It will create the delegate objects (step 3 in the discussion of delegates) and hook up the event handler. Class A will then pass the delegate objects to class B. When an event occurs in Class B, it will execute the event handler method in Class A.

Following is the sample code demonstrating events.

Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. namespace EventsHelloWorld
  7. {
  8.     public class myeventargs : EventArgs
  9.     {
  10.         public string str;
  11.     }
  12.  
  13.     //To declare an event inside a class, first a delegate type for the event must be declared,
  14.     //if none is already declared. The delegate type defines the set of arguments that are passed
  15.     //to the method that handles the event. Multiple events can share the same delegate type,
  16.     //so this step is only necessary if no suitable delegate type has already been declared.
  17.  
  18.         
  19.     //Create delegate object
  20.     //1st parameter: specifies the object that fired the event
  21.     //2nd arameter: holds data that can be used in the event handler
  22.     public delegate void mydelegate(object obj, myeventargs e);
  23.  
  24.     class A
  25.     {
  26.         //Create event handler methods
  27.         public void myfunction1(object obj, myeventargs e)
  28.         {
  29.             Console.WriteLine("Hello World {0}", e.str);
  30.         }
  31.  
  32.         //create delegates, plug in the handler and register with the object that will fire the events
  33.         //Hooking up to an event:   From outside the class that declared it, an event looks like a field,
  34.         //but access to that field is very restricted. The only things that can be done are:
  35.         //Compose a new delegate onto that field & Remove a delegate from a (possibly composite) field.
  36.  
  37.         public A(B b)
  38.         {
  39.             //To begin receiving event invocations, client code first creates a delegate of the event type
  40.             //that refers to the method that should be invoked from the event.
  41.             //Then it composes that delegate onto any other delegates that the event might be connected to using +=.
  42.             b.event1 += new mydelegate(myfunction1);
  43.         }
  44.  
  45.     }
  46.  
  47.     //Calls the encapsulated methods through the delegates (fires events)
  48.     class B
  49.     {
  50.         //The event itself is declared. An event is declared like a field of delegate type,
  51.         //except that the keyword event precedes the event declaration, following the modifiers.
  52.         public event mydelegate event1;
  53.  
  54.         public void fireevent(myeventargs eventargs)
  55.         {
  56.             //Once a class has declared an event, it can treat that event just like a field of the
  57.             //indicated delegate type. The field will either be null, if no client has hooked up
  58.             //a delegate to the event, or else it refers to a delegate that should be called when the event is invoked.
  59.             if (event1 != null)
  60.             {
  61.                 //Invoking an event is generally done by first checking for null and then calling the event.
  62.                 //Invoking an event can only be done from within the class that declared the event.
  63.                 event1(this, eventargs);
  64.             }
  65.         }
  66.  
  67.     }
  68.  
  69.     class Program
  70.     {
  71.         static void Main(string[] args)
  72.         {
  73.             myeventargs args1 = new myeventargs();
  74.             args1.str = "!!! Kishore";
  75.             B b = new B();
  76.             A a = new A(b);
  77.  
  78.             b.fireevent(args1);
  79.         }
  80.     }
  81. }
 

An event is a message sent by an object to signal the occurrence of an action. The action could be caused by user interaction, such as a mouse click, or it could be triggered by some other program logic. The object that raises (triggers) the event is called the event sender. The object that captures the event and responds to it is called the event receiver.

In event communication, the event sender class does not know which object or method will receive (handle) the events it raises. What is needed is an intermediary (or pointer-like mechanism) between the source and the receiver. The .NET Framework defines a special type (Delegate) that provides the functionality of a function pointer.

A delegate is a class that can hold a reference to a method. Unlike other classes, a delegate class has a signature, and it can hold references only to methods that match its signature. A delegate is thus equivalent to a type-safe function pointer or a callback. While delegates have other uses, the discussion here focuses on the event handling functionality of delegates. The following example shows an event delegate declaration.

Event delegates are multicast, which means that they can hold references to more than one event handling method. Delegates allow for flexibility and fine-grain control in event handling. A delegate acts as an event dispatcher for the class that raises the event by maintaining a list of registered event handlers for the event.

About these ads

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s