You are on page 1of 28

C# - Delegates and Events

First Edition, 2012

Pyongwon Lee http://bubblogging.wordpress.com/

C# - Delegates and Events

Contents

1. Introduction .............................................................................................. 3 2. Delegates in a Basic Way (C# 1) .................................................................. 4 3. Delegates in a Simple Way (C# 2) ................................................................ 6 4. Delegates with LINQ (C# 3) ........................................................................ 8 5. Invoking Multiple Methods ......................................................................... 10 6. Observer Pattern ...................................................................................... 12 7. Events .................................................................................................... 18 8. Actions, Predicates, and Functions .............................................................. 21 9. Asynchronous Delegates ........................................................................... 25

2012 by Pyongwon Lee

C# - Delegates and Events

1. Introduction
Have you ever heard of function pointers? If you have had a chance to study C or C++, you know what a pointer is. Just like the pointer to a value or an object, there is a pointer to a function. A function pointer has a reference to the functions entry point so that you can invoke the function not by a function name but by a pointer variable. A function pointer is very useful when you use the call-back feature. JavaScript uses it very often. For example, when you make an asynchronous call to the server, you pass the handler functions pointer with the request call. When the server is ready to respond, the server just calls the handler using the function pointer.

In C#, this functionality is accomplished by delegates. A delegate is an object that points to another method (both static and instance). It manages the address of (or a reference to) a method, a list of arguments, and a return value.

This booklet explains the followings:

Delegates (C#1, C#2, and C#3) Events Lambda Expressions Observer Pattern

2012 by Pyongwon Lee

C# - Delegates and Events

2. Delegates in a Basic Way (C# 1)


You definitely do not use the delegate syntax of C# 1 because later syntax is much compact and lucid. But C# 1syntax helps you understand what a delegate is and how it works. In order to use a delegate, you need to do the followings: Declare a delegate type Create/Find an appropriate method Create an instance of a delegate type Invoke a delegate instance

2.1 Declaring the Delegate Type


The first step to use delegates is to declare the delegate type. The delegate keyword is used to define a delegate type. The delegate definition looks like a definition of a method. Once a delegate type is defined, you can create an instance of the delegate type and use it to point to a method that has the same signature as the delegate type. Lets create a delegate type which points to any method that gets 3 integer parameters and returns an integer value.

public delegate int DoSomethingWithInt(int x, int y, int z);

2.2 Creating a Method


There is no use of a delegate type if you do not have any method to point to. Delegates can point to both instance methods and static methods.

2012 by Pyongwon Lee

C# - Delegates and Events

public static class IntFun { public static int GetTotal(int x, int y, int z) { return (int)(x + y + z); } public static int GetAverage(int x, int y, int z) { return (int)((x + y + z) / 3); } }

2.3 Creating an Instance of a Delegate Type


Defining a delegate type is like defining a class. Until you create an instance of it, it wont do anything. I want to use the DoSomethingWithInt delegate to point to the GetTotal() or GetAverage() method. To do this, you need to create a delegate object.

DoSomethingWithInt fp = new DoSomethingWithInt(IntFun.GetTotal); DoSomethingWithInt fp1 = new DoSomethingWithInt(IntFun.GetAverage);

Note that the name of the method is passed as an argument to the delegate type constructor.

2.4 Invoking a Delegate Instance


Now the final step!!! How can we call the method from a delegate object? Internally, a delegate object has the Invoke() method. But in C#, you do not call the Invoke() method directly (You can do it if you want). You can use a name of a delegate object as it is a method name.

Console.WriteLine("Total = {0}", fp(10, 20, 30)); // 60 Console.WriteLine("Average = {0}", fp1(10, 20, 30)); // 20

2012 by Pyongwon Lee

C# - Delegates and Events

3. Delegates in a Simple Way (C# 2)


You definitely do not use the delegate syntax of C# 1 because the later syntax is much compact and lucid. But C# 1syntax helps you understand what a delegate is and how it works.

3.1 Generic Delegates


The most important change of C# 2 is generics. Now you can also create generic delegates.

public delegate T Adder<T>(T x, T y); public static class GenericFun { public static int AddInt(int x, int y) { return x + y; } public static string AddNumber(string x, string y) { double a = Double.Parse(x); double b = Double.Parse(y); return (a + b).ToString(); } } public static class DelegateTest { public static void Test() { Adder<int> fp = new Adder<int>(GenericFun.AddInt); Adder<string> fp1 = new Adder<string>(GenericFun.AddNumber); Console.WriteLine("2 + 3 = {0}", fp(2, 3)); // 5 Console.WriteLine("1.4 + 4.7 = {0}", fp1("1.4", "4.7")); } }

// 6.1

2012 by Pyongwon Lee

C# - Delegates and Events

3.2 Creating a Delegate Instance Implicitly


When you create a delegate instance, you need to use the following syntax.

Adder<int> fp = new Adder<int>(GenericFun.AddInt);

C# 2 introduced a new syntax sugar. You can create a delegate instance implicitly.
Adder<int> fp = GenericFun.AddInt;

3.3 Anonymous Methods


When you add a method to a delegate, you need to define the method in somewhere else. For your convenience, C# 2 provides another construct to declare a method without a name. The delegate keyword is used again to declare an anonymous method. The syntax of an anonymous method is simple: delegate (parameters) { method body }

The only thing you need to care is that you cannot specify a return type. Just make sure you return the right type in the method body. The anonymous method works with generic types too.

public delegate T Adder<T>(T x, T y); public static void TestAnonymous() { Adder<int> fp = new Adder<int>(delegate(int x, int y) { return x + y; }); Console.WriteLine("2 + 3 = {0}", fp(2, 3)); // 5 }

You can create a delegate instance implicitly like this.

Adder<int> fp = delegate(int x, int y) { return x + y; };


2012 by Pyongwon Lee

C# - Delegates and Events

4. Delegates with LINQ (C# 3)


THE most important single feature of C#3 is LINQ. Many new features are added to C# 3 but they are mainly used with and for LINQ. In C# 3, you can use lambda expressions to use delegates. Though the syntax might look weird at first, it will become natural in time.

4.1 Lambda Expressions


The most important change of C# 2 is generics. Now you can also create generic delegates. As with the introduction of LINQ in C#, the anonymous method became more important than before. Therefore, C# introduces the shorter syntax to the anonymous method. Lambda Expressions use the lambda operator =>. The left side of the lambda operator specifies the input parameters and the right side holds the expression or statement block.

4.1.1 Expression Lambdas (input parameters) => expression

(x, y) => x == y (string s, int x) => (s.Length <= x) If you do not specify the type of a parameter, the compiler will infer the type.

4.1.2 Statement Lambdas (input parameters) => {statements;}

s => { Console.WriteLine(s); }
2012 by Pyongwon Lee

C# - Delegates and Events

4.1.3 Example Lets rewrite the generic anonymous method using a Lambda operator.

public delegate T Adder<T>(T x, T y); public static void TestLambda() { Adder<int> fp = (x, y) => { return x + y; }; Console.WriteLine("2 + 3 = {0}", fp(2, 3)); // 5 }

2012 by Pyongwon Lee

10

C# - Delegates and Events

5. Invoking Multiple Methods


Delegates can invoke multiple methods by a single call from a delegate object. This feature is called Multicasting and makes it possible to add multiple handlers to an event. Internally a delegate is a sealed class derived from the System.MulticastDelegate class, which is also derived from the System.Delegate class. The += and -= operators are overridden for delegates to add or remove a method into/from a delegate object.

public delegate void Greet(); public static class GreetFun { public static void Test() { Greet fp = () => { Console.WriteLine("Hello"); }; fp += () => { Console.WriteLine("Nice weather!"); }; fp += () => { Console.WriteLine("Bye"); }; fp(); // "Hello Nice weather! Bye" } }

5.1 Looking inside the Delegate Object


The Delegate class provides the GetInvocationList() method to return the list of methods.
public abstract class Delegate { public Object Target { get; } // instance object for an instance method, null for a static method public MethodInfo Method { get; } public virtual Delegate[] GetInvocationList(); }

You can use the following code to see the list of methods that the delegate holds. Note that the Delegate class has 2 properties:

2012 by Pyongwon Lee

11

C# - Delegates and Events

public delegate T DoSomething<T> (T x, T y); public class DelegateTest { public static int AddInt(int x, int y) { return x + y; } public int SubtractInt(int x, int y) { return x - y; } public static void InvestigateDelegate() { DelegateTest test = new DelegateTest(); DoSomething<int> fp = DelegateTest.AddInt; // static fp += test.SubtractInt; // instance fp += (x, y) => { return x * y; }; // anonymous, static foreach (Delegate d in fp.GetInvocationList()) { Console.WriteLine("Type = {0}, Method = {1}", d.Target, d.Method); } } }

2012 by Pyongwon Lee

12

C# - Delegates and Events

6. Observer Pattern
Before moving on to the Events, lets implement the Observer pattern.

6.1 Observer Pattern


The Observer (or Publisher/Subscriber) pattern is a software design pattern in which an object, called the publisher (subject), maintains a list of its dependents, called subscribers (observers), and notifies them automatically of any state changes. It is mainly used to implement event handling systems.

<Figure 1> Observer Pattern

Delegates are excellent choice for a publisher or a subject because it can hold multiple methods and call them at once.

6.2 Implementing the Observer Pattern without Delegates


Lets implement a simple notification scenario using delegates. Stock investors want to get notified of the up-to-date stock index. You need at least 2 classes: Publisher and Subscriber.

// Publisher public class StockMarket


2012 by Pyongwon Lee

13

C# - Delegates and Events

{ public int stockIndex = 0; public int StockIndex { get { return stockIndex; } set { stockIndex = value; // Notify to the subscribers } } } // Subscriber public class Investor { public void Notified(int stockIndex) { Console.WriteLine("From a Investor: Got it! The index is {0}", stockIndex); } }

When the stock index is changed, the publisher (stock market) wants to notify the change to its subscribers (investors). To accomplish this, the following features will be implemented. A publisher needs to take care of a list of subscribers. Subscribers can subscribe to or unsubscribe from the notification list. A publisher sends a notification message to all subscribers whenever the notification is required.

If you specify a specific class type when you create a list of subscribers, you are restricted to use only one type of objects as a subscriber. So it is a good idea to use an abstract class or, better, an interface. What if financial advisors also want to get a current stock index?

// Subscriber public interface IStockNotifiable { void Notified(int stockIndex); } public class Investor : IStockNotifiable { public void Notified(int stockIndex) { Console.WriteLine("From a Investor: Got it! The index is {0}", stockIndex); } } public class FinancialAdvisor : IStockNotifiable
2012 by Pyongwon Lee

14

C# - Delegates and Events

{ public void Notified(int stockIndex) { Console.WriteLine("From a Financial Advisor: Got it! The index is {0}", stockIndex); } }

OK, now the publisher class needs to have a list of subscribers and provide a way to add/remove a subscriber to/from the list.

// Publisher public class StockMarket { private List<IStockNotifiable> subscribers = new List<IStockNotifiable>(); public void Subscribe(IStockNotifiable subscriber) { subscribers.Add(subscriber); } public void Unsubscribe(IStockNotifiable subscriber) { subscribers.Remove(subscriber); } }

Finally, when the stock index is changed, the publisher should notify the change to subscribers.

// Publisher public class StockMarket { public int stockIndex = 0; public int StockIndex { get { return stockIndex; } set { stockIndex = value; // Notify to the subscribers foreach (IStockNotifiable subscriber in subscribers) { subscriber.Notified(stockIndex); } } } }

2012 by Pyongwon Lee

15

C# - Delegates and Events

If you are familiar with the LINQ syntax, you can use the code this like:
set { stockIndex = value; // Notify to the subscribers subscribers.ForEach(s => s.Notified(stockIndex)); }

Note that how the lambda expression can be used with the LINQ operators.

So far you have created interfaces and classes like this. Compare this with the <Figure 1>.

<Figure 2> Observer pattern without delegates

Lets test the code.

public static class Tester { public static void Test() { StockMarket market = new StockMarket(); Investor investor1 = new Investor(); market.Subscribe(investor1); Investor investor2 = new Investor(); market.Subscribe(investor2); FinancialAdvisor investor3 = new FinancialAdvisor(); market.Subscribe(investor3); // Stock Index is changed. market.StockIndex = 100;
2012 by Pyongwon Lee

16

C# - Delegates and Events

// Unsubscribe market.Unsubscribe(investor2); // StockPrice is changed. market.StockIndex = 95; } }

6.3 Implementing the Observer Pattern with Delegates


You can implement the Observer patter without delegates. It works, but you need to manage a list of subscribers by yourself in your publisher class. The delegates can hold multiple methods, so we can let a delegate to handle the subscriber list. You do not need to modify the observer interface and its concrete classes. As a first step, a delegate type needs to be declared with the matching method prototype.

public delegate void StockIndexHandler(int stockIndex);

Inside the publisher class, an instance of a delegate is created instead of a list of subscribers. The Subscribe() and Unsubscibe() methods are also modified to use the delegate object.

// Publisher public class StockMarket { private StockIndexHandler subscribers = null; public void Subscribe(IStockNotifiable subscriber) { subscribers += subscriber.Notified; } public void Unsubscribe(IStockNotifiable subscriber) { subscribers -= subscriber.Notified; } }

Finally you can use the delegate object to notify the change to all subscribers.

2012 by Pyongwon Lee

17

C# - Delegates and Events

// Publisher public class StockMarket { public int stockIndex = 0; public int StockIndex { get { return stockIndex; } set { stockIndex = value; // Notify to the subscribers subscribers(stockIndex); } } }

When you run the code, the result will be the same.

2012 by Pyongwon Lee

18

C# - Delegates and Events

7. Events
Because the Observer pattern (with delegates) can be very useful in the event handling model of the UI programming, .Net Framework wanted to make it formal. In the event handling model, the event belongs to a publisher and handlers are subscribers. C# provides the event keyword. It is used with the delegate type. You can think that an event is a modifier to the delegate instance. The important thing to remember is that a delegate is a type like a class but an event is a field (rather a property) and should be located in a type.

public delegate void StockIndexHandler(int stockIndex); public class StockMarket { public event StockIndexHandler stockEvent; }

When you add the event keyword to a delegate field, the compiler does some additional actions: The complier creates addhandler and removehandler methods linked to the delegate type internally You can include a delegate instance in an interface (An interface cannot have a field). But an event can be included in an interface. Event handler invocation is restricted within the class that declares an event.

Lets look at the third point:

public delegate void StockIndexHandler(int stockIndex); public class StockMarket { public event StockIndexHandler stockEvent; public StockIndexHandler stockDelegate; private int stockIndex; public int StockIndex { get { return stockIndex; } set { stockEvent(stockIndex); } // ok } } public class EventFun {
2012 by Pyongwon Lee

19

C# - Delegates and Events

public static void Test() { StockMarket market = new StockMarket(); market.stockEvent(100);// this line casuses an compile time error market.stockDelegate(100); // ok } }

7.1 Implementing the Observer Pattern with Events


The basic scenario of using events is like this: Create a delegate type Create an event Subscribe to or Unsubscribe from the event (add or remove methods that conform to the delegate type to the event) When the event is raised, subscribers will be notified.

Here is the complete code to use events and delegates together.

public delegate void StockIndexHandler(int stockIndex); public interface IStockNotifiable { void Notified(int stockIndex); } public class Investor : IStockNotifiable { public void Notified(int stockIndex) { Console.WriteLine("From a Investor: Got it! The index is {0}", stockIndex); } } public class FinancialAdvisor : IStockNotifiable { public void Notified(int stockIndex) { Console.WriteLine("From a Financial Advisor: Got it! The index is {0}", stockIndex); } } public class StockMarket { public event StockIndexHandler stockEvent; public int stockIndex = 0; public int StockIndex
2012 by Pyongwon Lee

20

C# - Delegates and Events

{ get { return stockIndex; } set { stockIndex = value; if (stockEvent!= null) { stockEvent(stockIndex); // call event handlers } } } public void Subscribe(IStockNotifiable subscriber) { stockEvent += subscriber.Notified; } public void Unsubscribe(IStockNotifiable subscriber) { stockEvent -= subscriber.Notified; } } public static class EventFun { public static void Test() { StockMarket market = new StockMarket(); Investor investor1 = new Investor(); market.Subscribe(investor1); Investor investor2 = new Investor(); market.Subscribe(investor2); FinancialAdvisor investor3 = new FinancialAdvisor(); market.Subscribe(investor3); // Stock Index is changed. market.StockIndex = 100; // Unsubscribe market.Unsubscribe(investor2); // StockPrice is changed. market.StockIndex = 95; } }

2012 by Pyongwon Lee

21

C# - Delegates and Events

8. Actions, Predicates, and Functions


It might not be practical to declare delegate types with commonly used signatures. .NET Framework provides the ready-made delegate types for you. So you can use them without defining delegate types by yourself. Those pre-defined delegate types are very useful with anonymous methods and LINQ. Most LINQ operators accept delegates as parameters. So it is essential to define delegate types for them. There are 3 basic types of delegate types depending on what the return type is: Actions: do not return anything (void) Predicates: return true or false (bool) Functions: return the specified type

8.1 Actions
An action delegate encapsulates a method that does not return a value. .NET Framework provides 17 Action delegates whose parameters differ.

public public public . . . public

delegate void Action() delegate void Action<in T>(T arg) delegate void Action<in T1, in T2>(T1 arg1, T2 arg2) delegate void Action<in T1, in T2, ..., in T16>(T1 arg1, T2 arg2, ... , T16 arg16)

Generics play a very important role here. It is not practical to create delegate for all possible data types. All Action delegates are declared in the System namespace.

public static void SayHello(bool isWinForm) { Action<string> display; if (isWinForm) display = (s => { System.Windows.Forms.MessageBox.Show(s); }); else display = (s => { Console.WriteLine(s); }); display("Hello, World"); } public static void Test()
2012 by Pyongwon Lee

22

C# - Delegates and Events

{ SayHello(false); SayHello(true); }

8.2 Functions
A function delegate encapsulates a method that returns a value. 17 Func delegates are defined in .NET Framework.

public delegate public delegate public delegate . . . public delegate arg2, ... , T16

TResult Func(out TResult) TResult Func<in T, out TResult>(T arg) TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2) TResult Func<in T1, in T2, ..., in T16, out TResult>(T1 arg1, T2 arg16)

All Func delegates are declared in the System namespace.

public enum OperationType { Add, Subtract, Multiply } public static void DoMath(OperationType type, int x, int y) { Func<int, int, string> operation; switch(type) { case OperationType.Add: operation= ((i, j) => { return break; case OperationType.Subtract: operation= ((i, j) => { return break; case OperationType.Multiply: operation= ((i, j) => { return break; default: operation= ((i, j) => { return break; }

string.Format("{0} + {1} = {2}", i, i, i+j); } );

string.Format("{0} - {1} = {2}", i, i, i-j); } );

string.Format("{0} * {1} = {2}", i, i, i*j); } );

"N/A"; } );

2012 by Pyongwon Lee

23

C# - Delegates and Events

Console.WriteLine(operation(x, y)); } public static void Test() { DoMath(OperationType.Add, 10, 5); DoMath(OperationType.Subtract, 10, 5); DoMath(OperationType.Multiply, 10, 5); }

8.3 Predicates
A predicate is a special kind of functions that return bool. Therefore .NET Framework does not provide exhaustive number of predefined predicate delegates. In fact, there is only one delegate.

public delegate bool Predicate<in T>(T arg)

8.4 LINQ and Delegates


Most LINQ operators accept the IEnumerable<T> object as an input and return another IEnumerable<T> object. Some aggregate operators (Sum, Average, Count ) return numbers. Specific algorithms of LINQ operators depend on delegates. For example, the Where operator needs a delegate instance in order to filter the input collection. Lets look at the few examples.

public static IEnumerable<TSource> Where<TSource>( this IEnumerable<T> source, Func<TSource, bool> predicate ); public static IEnumerable<TResult> Select<TSource, TResult>( this IEnumerable<TSource> source, Func<TSource, TResult> selector );

2012 by Pyongwon Lee

24

C# - Delegates and Events

Both Where and Select operators require the Func delegate as a parameter. In most cases, you are providing an anonymous method using the lambda expression.

var numbers = Enumerable.Range(1, 10); var oddNumbers = numbers.Where(i => i % 2 == 1).Select(i => i); foreach (int i in oddNumbers) Console.WriteLine(i);

2012 by Pyongwon Lee

25

C# - Delegates and Events

9. Asynchronous Delegates
By default, delegates work synchronously. But you can use them asynchronously too. You do not even need to work with any types of System.Threading namespace.

9.1 BeginInvoke() and EndInvoke()


Declare any delegate type Create a delegate instance Check the available methods of the delegate instance variable using IntelliSense Check the signature of 2 methods: BeginInvoke() and EndInvoke()

public delegate int MathOp(int x, int y); public static class AsynchPattern { public static void Test() { MathOp addOp = ((x, y) => x + y); addOp. } }

<Figure 3> BeginInvoke()

2012 by Pyongwon Lee

26

C# - Delegates and Events

<Figure 4> EndInvoke()

public IAsyncResult BeginInvoke(int x, int y, AsyncCallback cb, object state); public int EndInvoke(IAsyncResult result);

If you look at the documentation of Delegate or MulticastDelegate, BeginInvoke() and EndInvoke() method do not exist. So what are they? When you create a delegate type, the compiler automatically adds these methods using the signature of the delegate type.

9.2 IAsyncResult Interface


The System.IAsyncResult interface represents the status of an asynchronous operation.

public interface IAsyncResult { Object AsyncState { get; } // the last parameter of BeginInvoke() WaitHandle AsyncWaitHandle { get; } // to wait for an asynchronous operation to complete bool CompletedSynchronously { get; } // false for asynchronous operation bool IsCompleted { get; } }

2012 by Pyongwon Lee

27

C# - Delegates and Events

9.3 Invoking a Method Asynchronously


Call the BeginInvoke() method to start the asynchronous operation Check whether the IAsyncResult.IsCompleted property is true Get the result by calling the EndInvoke() method

public static void Test() { MathOp addOp = ((x, y) => { System.Threading.Thread.Sleep(4000); return x + y; }); IAsyncResult result = addOp.BeginInvoke(10, 20, null, null); while (!result.IsCompleted) { // Wait until the operation is completed Console.WriteLine("Waiting..."); System.Threading.Thread.Sleep(1000); } int answer = addOp.EndInvoke(result); Console.WriteLine("{0} + {1} = {2}", 10, 20, answer); }

Easy, isnt it? But if we just need to wait until the operation ends what ,in the first place, do we need to use an asynchronously call?

9.4 AsyncCallback Delegate


The BeginInvoke() method has a AsyncCallback parameter. What is it for?

public delegate void AsyncCallback(IAsyncResult ar);

It is a delegate type that represents a method to be called when a corresponding asynchronous operation completes. When the call-back method is called, the System.Runtime.Remoting.Messaging.AsyncResult object is passed as an IAsyncResult argument.

2012 by Pyongwon Lee

28

C# - Delegates and Events

public class AsyncResult : IAsyncResult { public Object AsyncDelegate { get; } }

By using the AsyncDelegate property, you can get the result.

using System.Runtime.Remoting.Messaging;

public static void Test1() { MathOp addOp = ((x, y) => { System.Threading.Thread.Sleep(4000); return x + y; }); IAsyncResult result = addOp.BeginInvoke(10, 20, ar => { AsyncResult aResult = ar as AsyncResult; MathOp op = aResult.AsyncDelegate as MathOp; int answer = addOp.EndInvoke(ar); Console.WriteLine("{0} + {1} = {2}", 10, 20, answer); }, null); // Doing some work here Console.WriteLine("Doing Something..."); System.Threading.Thread.Sleep(5000); Console.WriteLine("Done..."); }

2012 by Pyongwon Lee

You might also like