Home > WPF > Delegate in WPF

Delegate in WPF


 

Introduction

In this article we will first try to understand what problem delegate solves, we will then create a simple delegate and try to solve the problem. Next we will try to understand the concept of multicast delegates and how events help to encapsulate delegates. Finally we understand the difference between events and delegates and also understand how to do invoke delegates asynchronously.

Once we are done with all fundamentals we will summarize the six important uses of delegates.

Abstraction problems of methods and functions

Before we move ahead and we talk about delegates let’s try to understand what problem does delegate solve. Below is a simple class named ‘ClsMaths’ which has a simple ‘Add’ function. This class ‘ClsMaths’ is consumed by a simple UI client. Now let’s say over a period of time you add subtraction functionality to the ‘ClsMaths’ class, your client need to change accordingly to accommodate the new functionality.
In other words addition of new functionality in the class leads to recompiling of your UI client.

In short the problem is that there is a tight coupling of function names with the UI client. So how can we solve this problem?. Rather than referring to the actual methods in the UI / client if we can refer an abstract pointer which in turn refers to the methods then we can decouple the functions from UI.
Later any change in the class ‘ClsMath’ will not affect the UI as the changes will be decoupled by the Abstract pointer. This abstract pointer can be defined by using delegates. Delegates define a simple abstract pointer to the function / method.

Ok, now that we have understood the tight coupling problem and also the solution, let’s try to understand how we can define a simple delegate.

How to declare a delegate?

To implement a delegate is a four step process declare, create, point and invoke.

The first step is to declare the delegate with the same return type and input parameters. For instance the below function ‘add’ has two input integer parameters and one integer output parameter.

private int Add(int i,int y)
{
return i + y;
}

So for the above method the delegate needs to be defined in the follow manner below. Please see the delegate keyword attached with the code.

// Declare delegate
public delegate int PointetoAddFunction(int i,int y);

The return type and input type of the delegate needs to be compatible, in case they are not compatible it will show the below error as shown in the image below.

The next step (second step) is to create a delegate reference.

// Create delegate reference
PointetoAddFunction myptr = null;

The third step is to point the delegate reference to the method, currently we need to point the delegate reference to the add function.

// Point the reference to the add method
myptr = this.Add;

Finally we need to invoke the delegate function using the “Invoke” function of the delegate.

// Invoke the delegate
myptr.Invoke(20, 10)

Below figure sums up how the above four step are map with the code snippet.

Solving the abstract pointer problem using delegates

In order to decouple the algorithm changes we can expose all the below arithmetic function through an abstract delegate.

So the first step is to add a generic delegate which gives one output and takes two inputs as shown in the below code snippet.

public class clsMaths
{
public delegate int PointerMaths(int i, int y);
}

The next step is to expose a function which takes in operation and exposes an attached delegate to the UI as shown in the below code snippet.

public class clsMaths
{
public delegate int PointerMaths(int i, int y);

public PointerMaths getPointer(int intoperation)
{
PointerMaths objpointer = null;
if (intoperation == 1)
{
objpointer = Add;
}
else if (intoperation == 2)
{
objpointer = Sub;
}
else if (intoperation == 3)
{
objpointer = Multi;
}
else if (intoperation == 4)
{
objpointer = Div;
}
return objpointer;
}
}

Below is how the complete code snippet looks like. All the algorithm functions i.e. ‘Add’ , ‘Sub’ etc are made private and only one generic abstract delegate pointer is exposed which can be used to invoke these algorithm function.

public class clsMaths
{
public delegate int PointerMaths(int i, int y);

public PointerMaths getPointer(int intoperation)
{
PointerMaths objpointer = null;
if (intoperation == 1)
{
objpointer = Add;
}
else if (intoperation == 2)
{
objpointer = Sub;
}
else if (intoperation == 3)
{
objpointer = Multi;
}
else if (intoperation == 4)
{
objpointer = Div;
}
return objpointer;
}

private int Add(int i, int y)
{
return i + y;
}
private int Sub(int i, int y)
{
return i - y;
}
private int Multi(int i, int y)
{
return i * y;
}
private int Div(int i, int y)
{
return i / y;
}
}

So at the client side the calls becomes generic without any coupling with the actual method names like ‘Add’ , ‘Sub’ etc.

int intResult = objMath.getPointer(intOPeration).Invoke(intNumber1,intNumber2);

Multicast delegates

In our previous example we have see how we can create a delegate pointer to a function or method. We can also create a delegate which can point to multiple functions and methods. If we invoke such delegate it will invoke all the methods and functions associated with the same one after another sequentially.
Below is the code snippet which attaches 2 methods i.e. method1 and method2 with delegate ‘delegateptr’. In order to add multiple methods and function we need to use ‘+=’ symbols. Now if we invoke the delegate it will invoke ‘Method1’ first and then ‘Method2’. Invocation happen in the same sequence as the attachment is done.

// Associate method1
delegateptr += Method1;
// Associate Method2
delegateptr += Method2;
// Invoke the Method1 and Method2 sequentially
delegateptr.Invoke();

So how we can use multicast delegate in actual projects. Many times we want to create publisher / subscriber kind of model. For instance in an application we can have various error logging routine and as soon as error happens in a application you would like to broadcast the errors to the respective components.

Simple demonstration of multicast delegates

In order to understand multicast delegates better let’s do the below demo. In this demo we have ‘Form1’, ‘Form2’ and ‘Form3’. ‘Form1’ has a multicast delegate which will propagate event to ‘Form2’ and ‘Form3’.

At the form level of ‘Form1’ (this form will be propagating events to form2 and form3) we will first define a simple delegate and reference of the delegate as shown in the code snippet below. This delegate will be responsible for broadcasting events to the other forms.

// Create a simple delegate
public delegate void CallEveryOne();

// Create a reference to the delegate
public CallEveryOne ptrcall=null;
// Create objects of both forms

public Form2 obj= new Form2();
public Form3 obj1= new Form3();

In the form load we invoke the forms and attach ‘CallMe’ method which is present in both the forms in a multicast fashion ( += ).

private void Form1_Load(object sender, EventArgs e)
{
// Show both the forms
obj.Show();
obj1.Show();
// Attach the form methods where you will make call back
ptrcall += obj.CallMe;
ptrcall += obj1.CallMe;
}

Finally we can invoke and broadcast method calls to both the forms.

private void button1_Click(object sender, EventArgs e)
{
// Invoke the delegate
ptrcall.Invoke();
}

Problem with multicast delegates – naked exposure

The first problem with above code is that the subscribers (form2 and form3) do not have the rights to say that they are interested or not interested in the events. It’s all decided by ‘form1’.
We can go other route i.e. pass the delegate to the subscribers and let them attach their methods if they wish to subscribe to the broadcast sent by ‘form1’. But that leads to a different set of problems i.e. encapsulation violation.
If we expose the delegate to the subscriber he can invoke delegate, add his own functions etc. In other words the delegate is completely naked to the subscriber.

Events – Encapsulation on delegates

Events help to solve the delegate encapsulation problem. Events sit on top of delegates and provide encapsulation so that the destination source can only listen and not have full control of the delegate object.
Below figure shows how the things look like:-
• Method and functions are abstracted /encapsulated using delegates
• Delegates are further extended to provide broadcasting model by using multicast delegate.
• Multicast delegate are further encapsulated using events.

Implementing events

So let’s take the same example which we did using multicast delegates and try to implement the same using events. Event uses delegate internally as event provides higher level of encapsulation over delegates.
So the first step in the publisher (‘Form1’) we need to define the delegate and the event for the delegate. Below is the code snippet for the same and please do notice the ‘event’ keyword.
We have defined a delegate ‘CallEveryOne’ and we have specified an event object for the delegate called as ‘EventCallEveryOne’.

public delegate void CallEveryone();
public event CallEveryone EventCallEveryOne;

From the publisher i.e. ‘Form1’ create ‘Form2’ and ‘Form3’ objects and attach the current ‘Form1’ object so that ‘Form2’ and ‘Form3’ will listen to the events. Once the object is attached raise the events.

Form2 obj = new Form2();
obj.obj = this;
Form3 obj1 = new Form3();
obj1.obj = this;
obj.Show();
obj1.Show();
EventCallEveryOne();

At the subscriber side i.e. (Form2 and Form3) attach the method to the event listener.

obj.EventCallEveryOne += Callme;

This code will show the same results as we have got from multicast delegate example.

Difference between delegates and events

So what’s really the difference between delegates and events other than the sugar coated syntax of events. As already demonstrated previously the main difference is that event provides one more level of encapsulation over delegates.
So when we pass delegates it’s naked and the destination / subscriber can modify the delegate. When we use events the destination can only listen to it.

Delegates for Asynchronous method calls

One of the other uses of delegates is asynchronous method calls. You can call methods and functions pointed by delegate asynchronously.
Asynchronous calling means the client calls the delegate and the control is returned back immediately for further execution. The delegate runs in parallel to the main caller. When the delegate has finished doing his work he makes a call back to the caller intimating that the function / subroutine has completed executing.

To invoke a delegate asynchronously we need call the ‘begininvoke’ method. In the ‘begininvoke’ method we need to specify the call back method which is ‘CallbackMethod’ currently.

delegateptr.BeginInvoke(new AsyncCallback(CallbackMethod), delegateptr);

Below is the code snippet for ‘CallbackMethod’. This method will be called once the delegate finishes his task.

static void CallbackMethod(IAsyncResult result)
{
int returnValue = flusher.EndInvoke(result);
}

The Second Change Event Example

Suppose you want to create a Clock class that uses events to notify potential subscribers whenever the local time changes value by one second. Here is the complete, documented example:

using System;
using System.Threading;

namespace SecondChangeEvent
{
/* ======================= Event Publisher =============================== */

// Our subject — it is this class that other classes
// will observe. This class publishes one event:
// SecondChange. The observers subscribe to that event.

public class Clock
{
 // Private Fields holding the hour, minute and second
private int _hour;
private int _minute;
private int _second;

 // The delegate named SecondChangeHandler, which will encapsulate
// any method that takes a clock object and a TimeInfoEventArgs
// object as the parameter and returns no value. It’s the
// delegate the subscribers must implement.

public delegate void SecondChangeHandler (
object clock,
TimeInfoEventArgs timeInformation
);

// The event we publish
public event SecondChangeHandler SecondChange;

 // The method which fires the Event
protected void OnSecondChange(
object clock,
TimeInfoEventArgs timeInformation
)
{
// Check if there are any Subscribers
if (SecondChange != null)
{
// Call the Event
SecondChange(clock,timeInformation);
}
}

 // Set the clock running, it will raise an
// event for each new second

public void Run()
{
for(;;)
{
// Sleep 1 Second
Thread.Sleep(1000);

// Get the current time
System.DateTime dt = System.DateTime.Now;

// If the second has changed
// notify the subscribers

if (dt.Second != _second)
{
// Create the TimeInfoEventArgs object
// to pass to the subscribers

TimeInfoEventArgs timeInformation =
new TimeInfoEventArgs(
dt.Hour,dt.Minute,dt.Second);

// If anyone has subscribed, notify them
OnSecondChange (this,timeInformation);
}

// update the state
_second = dt.Second;
_minute = dt.Minute;
_hour = dt.Hour;

}
}
}

// The class to hold the information about the event
// in this case it will hold only information
// available in the clock class, but could hold
// additional state information

public class TimeInfoEventArgs : EventArgs
{
public TimeInfoEventArgs(int hour, int minute, int second)
{
this.hour = hour;
this.minute = minute;
this.second = second;
}
public readonly int hour;
public readonly int minute;
public readonly int second;
}

 /* ======================= Event Subscribers =============================== */

// An observer. DisplayClock subscribes to the
// clock’s events. The job of DisplayClock is
// to display the current time

public class DisplayClock
{
// Given a clock, subscribe to
// its SecondChangeHandler event

public void Subscribe(Clock theClock)
{
theClock.SecondChange +=
new Clock.SecondChangeHandler(TimeHasChanged);

}

 // The method that implements the
// delegated functionality

public void TimeHasChanged(
object theClock, TimeInfoEventArgs ti)
{
Console.WriteLine(“Current Time: {0}:{1}:{2}”,
ti.hour.ToString(),
ti.minute.ToString(),
ti.second.ToString());
}
}

// A second subscriber whose job is to write to a file
public class LogClock
{
public void Subscribe(Clock theClock)
{
theClock.SecondChange +=
new Clock.SecondChangeHandler(WriteLogEntry);

}

// This method should write to a file
// we write to the console to see the effect

// this object keeps no state
public void WriteLogEntry(
object theClock, TimeInfoEventArgs ti)
{
Console.WriteLine(“Logging to file: {0}:{1}:{2}”,
ti.hour.ToString(),
ti.minute.ToString(),
ti.second.ToString());
}
}

/* ======================= Test Application =============================== */

// Test Application which implements the
// Clock Notifier – Subscriber Sample

public class Test
{
public static void Main()
{
// Create a new clock
Clock theClock = new Clock();

// Create the display and tell it to
// subscribe to the clock just created

DisplayClock dc = new DisplayClock();
dc.Subscribe(theClock);

// Create a Log object and tell it
// to subscribe to the clock

LogClock lc = new LogClock();
lc.Subscribe(theClock);

// Get the clock started
theClock.Run();
}
}
}

Summarizing Use of delegates

There are 6 important uses of delegates:-
1. Abstract and encapsulate a method (Anonymous invocation)

This is the most important use of delegates; it helps us to define an abstract pointer which can point to methods and functions. The same abstract delegate can be later used to point to that type of functions and methods. In the previous section we have shown a simple example of a maths class. Later addition of new algorithm functions does not affect the UI code.

2. Callback mechanism
Many times we would like to provide a call back mechanism. Delegates can be passed to the destination and destination can use the same delegate pointer to make callbacks.

3. Asynchronous processing
By using ‘BeginInvoke’ and ‘EndInvoke’ we can call delegates asynchronously. In our previous section we have explained the same in detail.

4. Multicasting – Sequential processing
Some time we would like to call some methods in a sequential manner which can be done by using multicast delegate. This is already explained in the multicast example shown above.

5. Events – Publisher subscriber model
We can use events to create a pure publisher / subscriber model.

Difference between delegates and C++ pointer

C++ pointers are not type safe, in other words it can point to any type of method. On the other hand delegates are type safe. A delegate which is point to a return type of int cannot point to a return type of string.

Why do we need delegates

Simple answer: the code needing to perform the action doesn’t know the method to call when it’s written. You can only call the method directly if you know at compile-time which method to call, right? So if you want to abstract out the idea of “perform action X at the appropriate time” you need some representation of the action, so that the method calling the action doesn’t need to know the exact implementation ahead of time.

For example:

  • Enumerable.Select in LINQ can’t know the projection you want to use unless you tell it
  • The author of Button didn’t know what you want the action to be when the user clicks on it
  • They also make certain designs cleaner (for example, instead of a switch statement where you call different methods, you call the delegate passed in) and easier to understand and allow for extending your code without changing it
  • Delegates are also the basis of the eventing system – writing and registering event handlers without delegates would be much harder than it is with them

Func and Action Delegates

The Func and Action generic delegates were introduced in the .NET framework version 3.5. They provide flexible delegates with generic parameters that can be used for many purposes, including passing lambda expressions to method parameters.

Generic Delegates

The .NET framework version 3.5 introduced two new sets ofgeneric, parameterised delegates named Func and Action. The Func delegate can be used to encapsulate a method that accepts between zero and four arguments and returns a value. The Action delegate also represents methods with zero to fourparameters but differs from Func in that the method must return void.

The new delegates can be used to reduce the number of delegates that you define explicitly. In situations where you would need a delegate that matches one of the predefined Func or Action signatures, you may decide to use the built-in version. You should consider the naming of the delegate however, as “Func” or “Action” may not express your intent as clearly as another name.

One of the key reasons for the introduction of Func and Action is their relationship with lambda expressions. Every lambda expression’s underlying type is one of these generic delegates. This means that lambda expressions can be passed to method parameters of the appropriate type without explicitly creating a delegate. Many of the LINQ standard query operators accept Func arguments to take advantage of this feature.

Func Delegate

There are five variations upon the Func delegate, each allowing a different number of parameters to be represented. In each case, the return value and the parameters are defined as generic types, allowing the types to vary according to the method that the delegate encapsulates. The five signatures are shown below. Note that the return value of the method is always the last element in the list of types.

public delegate TResult Func<TResult>()
public delegate TResult Func<T, TResult>(T arg)
public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2)
public delegate TResult Func<T1, T2, T3, TResult>(T1 arg1, T2 arg2, T3 arg3)
public delegate TResult Func<T1, T2, T3, T4, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4)

Using Func<TResult>

The most basic variant of Func is Func<TResult>. This delegate represents methods that return a value but have no parameters. We can demonstrate this with the following sample code. In the example, a new delegate is created using ananonymous method. The method returns a double-precision floating-point number containing a tax rate value.

Func<double> taxRate = delegate { return 17.5; };
Console.WriteLine("{0}%", taxRate());   // Outputs "17.5%"

We can create the same functionality using a lambda expression. Any lambda that accepts no arguments and returns a double is of the type Func<double>. To demonstrate, try executing the following:

Func<double> taxRate = () => 17.5;
Console.WriteLine("{0}%", taxRate());   // Outputs "17.5%"

Using Func with Parameters

Func can represent methods with up to four parameters. In the next example a Func delegate is used to reference an anonymous method that accepts three integer arguments and returns a long integer.

Func<int, int, int, long> multiply = delegate(int a, int b, int c) { return a * b * c; };
Console.WriteLine(multiply(2, 3, 4));   // Outputs "24"

Again, the code can be recreated using a lambda expression in place of the anonymous method. In this case, the three parameters are named a, b and c. The parameter types do not need to be included in the lambda expression as they, and the return type, are inferred by the compiler.

Func<int, int, int, long> multiply = (a, b, c) => a * b * c;
Console.WriteLine(multiply(2, 3, 4));   // Outputs "24"

Action Delegate

As with Func, the Action delegate has five variations. These allow the encapsulation of methods that have up to four parameters but do not return a value. Again, all of the parameters are generic types allowing any type to be used for each argument. The five signatures are as follows:

public delegate void Action()
public delegate void Action<T, >(T arg)
public delegate void Action<T1, T2>(T1 arg1, T2 arg2)
public delegate void Action<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3)
public delegate void Action<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4)

Using Action

The simplest Action delegate is used with methods that accept no parameters and return no values. The following code demonstrates this with an anonymous method that outputs text to the console.

Action showMessage = delegate { Console.WriteLine("Hello, world"); };
showMessage();                  // Outputs "Hello, world"

As with the Func delegates, Action can be used with lambda expressions. The equivalent of the previous example can be created using a statement lambda, as follows:

Action showMessage = () => { Console.WriteLine("Hello, world"); };
showMessage();                  // Outputs "Hello, world"

Using Action with Parameters

As a final example of the basic use of Action, consider the following example. This code uses a single string parameter for the delegate. The parameter accepts a message to be outputted to the console.

Action<string> showMessage = delegate(string msg) { Console.WriteLine(msg); };
showMessage("Hello, world");    // Outputs "Hello, world"

This example can be recreated using a statement lambda that includes a parameter. In this case the string parameter’s type is inferred.

Action<string> showMessage = (msg) => { Console.WriteLine(msg); };
showMessage("Hello, world");    // Outputs "Hello, world"

Using Generic Delegates as Parameter Types

The Func delegate is commonly used as the type for parameters of methods that accept lambda expressions. These include LINQ standard query operators and similar methods that you create in your own projects. We will demonstrate this with this article’s final example.

The following method defines an array containing the names of ten fruits. The method returns a filtered list of these fruits based upon the delegate passed as the only argument. Note that the types assigned to the Func delegate specify that the encapsulated method must receive a single string parameter and return a Boolean value. We could execute the passed method against each fruit string in a for-each loop. However, for simplicity the example uses the “Where” query operator.

private static string[] Fruit(Func<string, bool> filter)
{
    string[] fruit = new string[] { "Apple", "Banana", "Cherry", "Damson", "Elderberry", "Fig", "Grapefruit", "Huckleberry", "Lemon", "Mango" };
    return fruit.Where(filter).ToArray();
}

To test the method, add the following code to the Main method of a console application. This code calls the Fruit method, applying a filter that returns only fruit with a name shorter than six characters. The fruit names are then outputted.

string[] shortFruit = Fruit(f => f.Length < 6);
foreach (string fruit in shortFruit)
    Console.WriteLine(fruit);

/* OUTPUT Apple Fig Lemon Mango */


Advertisements
Categories: WPF Tags:
  1. No comments yet.
  1. No trackbacks yet.

Thanks for your comment

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

%d bloggers like this: