Вы находитесь на странице: 1из 48

Chapter - 7

Delegates and Events

Chapter Objectives

.NET Delegate Types Basic Concepts How to build a C# Delegate Example Multicasting with Delegates

Asynchronous Delegates
Events Basic Concepts

Listening to incoming Events

Examples on Events

.NET Delegate Types - Basic Concepts

Delegates, Interfaces and Events allow you to provide callback functionality. Each type has its own specific usage characteristics that make it better suited to particular situations. A delegate in C# is similar to a function pointer in C or C++. Function pointers simply represent memory address & they do not include type-safe info. Solution - C# implements Delegates, it implements the call back technique in a much safer & more object oriented manner. Delegate is an object that points to another method in the application All these happen at run time and not at compile time Unlike function pointers in C or C++, delegates are objectoriented, type-safe, and secure A delegate declaration defines a type that encapsulates name of the method a set of arguments a return type 3

Defining a Delegate in C#

Dictionary meaning a person acting for another person In C# - a method acting for another method A delegate in C# is a class type object & is used to invoke a method that has been encapsulated into it at the time of its creation (at run time). Delegates involve 4 steps Delegate declaration - defines using System.Delegate as a base class. Delegate method definition any function (defined in a class) whose signature matches the delegate signature exactly

Defining a Delegate
Delegate Instantiation holds reference to delegate methods Delegate invocation an instance is used to invoke the methods An interesting and useful property of a delegate is that it does not know or care about the class of the object that it references it can be used to hold reference to a method of any class all that matters is that the method's argument types and return type match the delegate's

Delegate Declaration


Modifier delegate return-type delegate-name(parameters);

Here, parameters identifies the signature of the delegate Eg: delegate void SimpleDelegate(); delegate int MathOperation (int x, int y); public delegate int ComareItems (object o1 , object o2);


Since delegate is a class type, it can be defined in 2. outside all classes

1. Inside a class

3. As the top level object in a namespace

Delegate types are implicitly sealed

Delegate Methods

The methods whose references are encapsulated into a delegate instance are known as delegate methods or clallback entities. The signature & return type of delegate methods must exactly match the signature & return type of the delegate

Eg: delegate void Delegate(); Can encapsulate references to the following methods:

Public void F1()

{ CW (F1); } Static public F2()

CW (F2);

Delegate Methods
Eg2: delegate double MathOp(double x, double y);

Can refer any of the following methods:

public static double multiply(double a double b) { {

return(a*b); } return(a/b); }

public double divide(double a double b)

Dont care whether the method is a static or an instance method


Delegate Instantiation

General form

new delegate-type(expression)
Here, delegate-type name of the delegate declared earlier whose object should be created

expression method name or a value of a delegate_type


If it is method name its signature & return type must be same as those of a delegate-type

Matching method can be static or instance method

If it is instance method, we need to specify the instance as well as name of the method If it is static method, then specify only class name and 10 method name

Delegate Instantiation

The method & object to which a delegate refers are determined when delegate is instantiated & then delegate remain constant for the entire lifetime of the delegate Therefore once delegate created cant be change Cant create delegate that refer constructor, indexer, or userdefined operator
class Delegate { static float Product(float a, float b) // sign not match { return(a*b); } }

Eg1: delegate int ProductDelegate(int x, int y);

static int Product (int a, int b)

{ return (a*b); // delegate instantiation

// Sign match

ProductDelegate P=new ProductDelegate(Product);


Delegate Instantiation
Eg12 delegate void DisplayDelegate(); class A { } class B { } //delegate instance static public void DisplayB() { CW(Display B): } Public void { DisplayA() } CW(Display A);

A a = new A();
DisplayDelegate d1=new DisplayDelegate(a.DisplayA); . DisplayDelegate d2 = new DisplayDelegate(B.DisplayB);

Delegate Invocation

General form

Here, parameter list is optional, provides value Eg1: delegate1(x,y); // void delegate

Returns void, so it cannot be used as a operand of any operator

Eg2: double result = delegate2( 2.56, 45.7);

Returns a value, so, it can be used as an operand for operator.


// Program to illustrate creating & implemting a delegate using system; Delegate int ArithOp( int x, int y); Class MathOperation { public static int Add(int a, int b) { return (a+b); { return (a-b); } Class Delegate_Test } }

public static int Sub(int a, int b)

public static void Main()

{ ArithOp Operation1 = new ArithOp(MathOperation.Add); ArithOp Operation2 = new ArithOp(MathOperation.Sub); // invoking delogates

int result1 = Operaion1( 200, 100);

int result2=Operation2(200,100); CW(Result1 = + result1); CW(Result2= +result2);

} }

Delegates vs. Interfaces

Delegates and interfaces are similar in that they enable the separation of specification and implementation Delegate specifies the signature of a method, and authors can write methods that are compatible with the delegate specification. Same is true even in the case of an interface when should you use delegate/interface?


Use Delegates 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 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

Use Interfaces 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

Delegate in C#

To declare a delegate, use the keyword delegate public delegate void ProcessBookDelegate(Book book);

C# automatically creates a new sealed class with three methods:

sealed class ProcessBookDelegate : system.MulticastDelegate {
public ProcessBookDelegate (object target, uint functionAddress);

public void Invoke (Book book); public IAsyncResult BeginInvoke (Book book); public void EndInvoke (IAsyncResult result);


A multicast delegate is one that can have more than one element in its invocation list. This means, a multicast delegate can point to multiple methods When a multicast delegate is invoked, the delegates in the invocation linked list are called synchronously in the order in which they appear Method - returns the name of the static method Target - gets the class instance on which the current delegate invokes the instance method Combine() adds a method to the list GetInvocationList() returns an array of System.Delegate types Remove() removes a delegate from the list

Example 1 Simple: Outline

AnyMethodTakingAString(string s) delegate represents an object that maintains a reference to some method that has a string type parameter and returns void When you want to call the method thro' the delegate, pass the name of the method to the delegates constructor AnyMethodTakingAString del; del = new AnyMethodTakingAString(PlainPrint); del("BIT"); Now PlainPrint() method will be invoked via the delegate

Example 1 - Simple
class DelegateApp { // This is the method that will be called by the delegate. public static void PlainPrint(string msg) { Console.WriteLine("Msg is: {0}", msg); } public static void UpperCasePrint(string msg) { Console.WriteLine("Msg is: {0}", msg.ToUpper()); } public static void XXXXYYYYZZZZ888777aaa(string msg) { Console.WriteLine("Msg is: {0}", msg); } // Bad target! Why? public void BadTargetForDelegate(int x, AppDomain y) { // Stuff. } // Define a delegate.

public delegate void AnyMethodTakingAString(string s);


public static void Main() { Console.WriteLine("***** A very simple delegate example *****\n"); // Make the delegate. AnyMethodTakingAString del; del = new AnyMethodTakingAString(PlainPrint); del("Hello there..."); Console.WriteLine("->I just called: {0}\n", del.Method); // Reassign and invoke delegate. del = new AnyMethodTakingAString(UpperCasePrint); del("Hello there..."); Console.WriteLine("->I just called: {0}\n", del.Method); // Reassign and invoke delegate. del = new AnyMethodTakingAString(XXXXYYYYZZZZ888777aaa); del("Hello there..."); Console.WriteLine("->I just called: {0}\n", del.Method); } }

Example - 2 (Sorting)
DelegateTest a int array bool AscSort(e1, e2) bool DescSort(e1, e2) DelegateBSort.bubbleSort(a, new DelegateBSort.Comp(AscSort)) DelegateBSort.bubbleSort(a, new DelegateBSort.Comp(DescSort))

DelegateBSort delegate bool Comp(e1, e2) static void bubbleSort(a, Comp c)


public class DelegateBSort {

public delegate bool Comp(int e1, int e2);

public static void Swap(ref int x, ref int y) { int t; t = x; x = y; y = t; } public static void bubbleSort(int[ ] a, Comp c) { for(int i = 0; i < a.Length - 1; i++) for(int j = 0; j < a.Length - 1 - i; j++) { if (c(a[ j ], a[ j+1 ])) Swap(ref a[ j ], ref a[ j+1 ]); } } }

public class DelegateTest { private int[ ] a = new int[4] {10, 40, 20, 30}; // method to be called for ascending order public bool AscSort(int e1, int e2) { return e1 > e2; } // method to be called for descending order public bool DescSort(int e1, int e2) { return e1 < e2; } public void DispAscArr( ) { // Delegate the sorting to ascending order DelegateBSort.bubbleSort(a, new DelegateBSort.Comp(AscSort)); for (int i = 0; i < a.Length ; i++) Console.WriteLine(a[i]); } public void DispDescArr( ) { // Delegate the sorting to descending order DelegateBSort.bubbleSort(a, new DelegateBSort.Comp(DescSort)); for (int i = 0; i < a.Length ; i++) Console.WriteLine(a[i]); } }

public static void Main(string[ ] args) { DelegateTest obj = new DelegateTest ( ); Console.WriteLine("Ascending Array: "); obj.DispAscArr ( ); Console.WriteLine("Descending Array: "); obj.DispDescArr ( ); }

Example - 3 (Abstract Model)

Main( ) g.ProcessCars(new Car.CarDelegate(WashCar) ) static void WashCar(Car c) static void RotateTires(Car c)

Garage theCars - ArrayList void ProcessCars(Car.CarDelegate proc)

Car isDirty shouldRotate delegate void CarDelegate (Car c)


Example 3 - Car

The Car class is modified by adding two more members and properties:
private bool isDirty; private bool shouldRotate; public bool Dirty { get{ return isDirty; } set{ isDirty = value; } } public bool Rotate { get{ return shouldRotate; } set{ shouldRotate = value; } }

Now, we shall declare a delegate as public delegate void CarDelegate( Car c);

Garage Class

This delegate encapsulates a function pointer to some method taking a Car and returning void.
public class Garage { // We have some cars. ArrayList theCars = new ArrayList(); public Garage() { theCars.Add(new Car("Viper", 100, 0, true, false)); theCars.Add(new Car("Fred", 100, 0, false, false)); theCars.Add(new Car("BillyBob", 100, 0, false, true)); theCars.Add(new Car("Bart", 100, 0, true, true)); theCars.Add(new Car("Stan", 100, 0, false, true)); }

Garage Class

This method takes a CarDelegate as a parameter. Therefore, 'proc' is nothing more than a function pointer... public void ProcessCars(Car.CarDelegate proc) { . foreach(Car c in theCars) proc(c); }

CarApp Class
public class ServiceDept { // A target for the delegate. public static void WashCar(Car c) { if(c.Dirty) Console.WriteLine("Cleaning a car"); else Console.WriteLine("This car is already clean..."); } // Another target for the delgate. public static void RotateTires(Car c) { if(c.Rotate) Console.WriteLine("Tires have been rotated"); else Console.WriteLine("Don't need to be rotated..."); } }


One way of using the delegates (because WashCar and RotateTires are static methods) Garage g = new Garage(); // Wash all dirty cars. g.ProcessCars(new Car.CarDelegate(WashCar)); // Rotate the tires. g.ProcessCars(new Car.CarDelegate(RotateTires)); If they are non-static, then use the following syntax: ServiceDept sd = new ServiceDept(); g.ProcessCars(new Car.CarDelegate(sd.WashCar)); g.ProcessCars(new Car.CarDelegate(sd.RotateTires));


Multicast delegate can call any number of Methods For multicasting you can use + operator which is overloaded
Garage g = new Garage();
// Create two new delegates Car.CarDelegate w = new Car.CarDelegate (WashCar)); Car.CarDelegate r = new Car.CarDelegate (RotateTires)); g.ProcessCars(w + r);


An event is an action which you can respond to, or "handle," in code Events can be generated by a user action, such as clicking the mouse or pressing a key; by program code; or by the system (e.g. Mouse Click event) Events are widely used in GUI-based programming (VB6.0) Buttons, TextBox, ComboBox, etc report back to the enclosing Form (Listener). How is it useful in C# ? Take for example the Car class. When some thing goes abnormal (excess speed) an exception is raised and the program may halt! A better solution is to inform the object for some action. Delegates are classes commonly used within the .NET Framework to build event-handling mechanisms.

Establishing Events

Events, however, need not be used only for graphical interfaces Events provide a generally useful way for objects to signal state changes that may be useful to clients of that object Events are an important building block for creating classes that can be reused in a large number of different programs Declaration of events is a two step process:

define a delegate when an event occurs, the methods held by the delegate will be executed

Define the events using C# 'event' keyword


Basic Event Model


Abstract Model
Declaring an Event (Car Class) public delegate void EngineHandler(string msg); public static event EngineHandler Exploded; public static event EngineHandler AboutToBlow;

Invoking / Firing an Event if(Exploded != null) Exploded("Sorry, this car is dead...");

Hooking up / Subscribing to an Event Car.Exploded += new Car.EngineHandler(sink.OnBlowUp); Car.AboutToBlow += new Car.EngineHandler(sink.OnAboutToBlow);


Car Example Declaring an Event

public class Car

// The delegate for our events. public delegate void EngineHandler(string msg); // This car can send these events. public static event EngineHandler Exploded; public static event EngineHandler AboutToBlow; }

public void SpeedUp(int delta) { // If the car is dead, send event. if(carIsDead) { if(Exploded != null) Exploded("Sorry, this car is dead..."); } else { currSpeed += delta; // Almost dead? if(10 == maxSpeed - currSpeed) if(AboutToBlow != null) AboutToBlow("Careful, approaching terminal speed!"); // Still OK! if(currSpeed >= maxSpeed) carIsDead = true; else Console.WriteLine("--> CurrSpeed = {0}", currSpeed); } }


Listening to Car Events

public class CarEventSink { // OnBlowUp event sink A. public void OnBlowUp(string s) { Console.WriteLine("Message from car: {0}", s); } public void OnAboutToBlow(string s) { Console.WriteLine("Message from car: {0}", s); } }


public class CarApp { public static int Main(string[] args) { // Make a car as usual. Car c1 = new Car("SlugBug", 100, 10); // Make sink object. CarEventSink sink = new CarEventSink(); // Hook into events. Console.WriteLine("***** Hooking into car events *****"); Car.Exploded += new Car.EngineHandler(sink.OnBlowUp); Car.AboutToBlow += new Car.EngineHandler(sink.OnAboutToBlow); // Detach from events. Car.Exploded -= new Car.EngineHandler(sink.OnBlowUp); }

Bank Example

An event will be raised when an overdraft occurs (you may use a callback delegate also) Account Class OverDraftEventHandler Delegate OnOverdraftHandler Event Clients can subscribe using the following methods:

AddOnOverdraft RemoveOnOverdraft

EventArgs - is a base class for event argument classes. In case if your event has to pass additional information to its subscribers, a subclass is needed

Class Account
class Account { protected int balance; // sender - ref to the object that raised the event // e - which describes the event - contains event arguments public delegate void OverdraftEventHandler (object sender, OverdraftEventArgs e); public event OverdraftEventHandler OnOverdraftHandler;

public Account(int bal) { balance = bal; }


public int Balance { get { return balance; } } public void Deposit(int aDeposit) { if (aDeposit < 0) throw new ArgumentOutOfRangeException(); balance = balance + aDeposit; } public bool Withdrawl(int aDebit) { if(aDebit < 0) throw new ArgumentOutOfRangeException(); if(aDebit < balance) { balance = balance - aDebit; return true; } OverdraftEventArgs args = new OverdraftEventArgs(balance, aDebit); OnOverdraft(args); return false; }


public void AddOnOverdraft(OverdraftEventHandler handler) { OnOverdraftHandler += handler; } public void RemoveOnOverdraft (OverdraftEventHandler handler) { OnOverdraftHandler -= handler; } protected void OnOverdraft(OverdraftEventArgs e) { if(OnOverdraftHandler != null) OnOverdraftHandler(this, e); // firing the event } }

public class OverdraftEventArgs : EventArgs { protected int balance; protected int withdrawl; public OverdraftEventArgs(int bal, int wd) { balance = bal; withdrawl = wd; } public int Balance { get { return balance; } } public int Withdrawl { get { return withdrawl; } } }


public class Client { public static void OnOverdraft(object sender, OverdraftEventArgs e) { Console.WriteLine("An overdraft occurred"); Console.WriteLine("The account balance is = {0}",e.Balance); Console.WriteLine("The with drawl was = {0}",e.Withdrawl); } public static void Main(string[] args) { Account myAccount = new Account(100); Account.OverdraftEventHandler h = null; h = new Account.OverdraftEventHandler(OnOverdraft); myAccount.AddOnOverdraft(h); } }

End of Chapter 7