C Sharp Programming/Delegates and Events

From Wikibooks, the open-content textbooks collection

Jump to: navigation, search

Contents

[edit] Introduction

Events and delegates are fundamental to any Windows or Web Application. These allow the developer to "subscribe" to particular actions carried out by the user. Therefore instead of expecting everything and filtering out what you want, you choose what you want to be notified of and react to that action.

A delegate is a way of telling C# which method to call when an event is triggered. For example, if you click a Button on a form, the program would call a specific method. It is this pointer which is a delegate. Delegates are good because you can notify several methods that an event has occurred, if you so wish.

An event is a notification by the .NET framework that an action has occurred. Each event contains information about the specific event, e.g., a mouse click would say which mouse button was clicked and where on the form it was clicked.

Lets say you write a program that only reacts to a Button click, here is the sequence of events that occurs:

  • User presses the mouse down on a button
    • The .NET framework raises a MouseDown event
  • User releases the mouse button
    • The .NET framework raises a MouseUp event
    • The .NET framework raises a MouseClick event
    • The .NET framework raises a Clicked event on the Button

Since the button's click event has been subscribed, the rest of the events are ignored by the program and your delegate tells the .NET framework which method to call, now that the event has been raised.

[edit] Delegates

Delegates are a construct for abstracting and creating objects that reference methods and can be used to call those methods. Delegates form the basis of event handling in C#. A delegate declaration specifies a particular method signature. References to one or more methods can be added to a delegate instance. The delegate instance can then be "called" which effectively calls all the methods that have been added to the delegate instance. A simple example:

using System;
delegate void Procedure();

class DelegateDemo
{	
    static void Method1()
    {
         Console.WriteLine("Method 1");
    }

    static void Method2()
    {
         Console.WriteLine("Method 2");
    }

    void Method3()
    {
         Console.WriteLine("Method 3");
    }

    static void Main()
    {
         Procedure someProcs = null;
         someProcs += new Procedure(DelegateDemo.Method1);
         someProcs += new Procedure(DelegateDemo.Method2);
         DelegateDemo demo = new DelegateDemo();
         someProcs += new Procedure(demo.Method3);
         someProcs();
    }
}

In this example, the delegate is declared by the line delegate void Procedure(); This statement is a complete abstraction. It does not result in executable code that does any work. It merely declares a delegate type called Procedure which takes no arguments and returns nothing. Next, in the Main() method, the statement Procedure someProcs = null; instantiates a delegate. Something concrete has now been created. The assignment of null to someProcs means that the delegate is not initially referencing any methods. The statements someProcs += new Procedure(DelegateDemo.Method1); and someProcs += new Procedure(DelegateDemo.Method2); add two static methods to the delegate instance. (Note: the class name could have been left off of DelegateDemo.Method1 and DelegateDemo.Method2 because the statement is occurring in the DelegateDemo class.) The statement someProcs += new Procedure(demo.Method3); adds a non-static method to the delegate instance. For a non-static method, the method name is preceded by an object reference. When the delegate instance is called, Method3() is called on the object that was supplied when the method was added to the delegate instance. Finally, the statement someProcs(); calls the delegate instance. All the methods that were added to the delegate instance are now called in the order that they were added.

Methods that have been added to a delegate instance can be removed with the -= operator:

 someProcess -= new Procedure(DelegateDemo.Method1);

In C# 2.0, adding or removing a method reference to a delegate instance can be shortened as follows:

    someProcess += DelegateDemo.Method1;
    someProcess -= DelegateDemo.Method1;

Invoking a delegate instance that presently contains no method references results in a NullReferenceException.

Note that if a delegate declaration specifies a return type and multiple methods are added to a delegate instance, then an invocation of the delegate instance returns the return value of the last method referenced. The return values of the other methods cannot be retrieved (unless explicitly stored somewhere in addition to being returned).

[edit] Anonymous Delegates

Anonymous delegates are a short way to write delegate code, specified using the delegate keyword. The delegate code can also reference local variables of the function in which they are declared. Anonymous delegates are automatically converted into methods by the compiler. For example:

using System;
delegate void Procedure();
 
class DelegateDemo2
{
    static Procedure someProcs = null;
 
    private static void AddProc()
    {
        int variable = 100;
 
        someProcs += new Procedure(delegate
            {
               Console.WriteLine(variable);
            });
    }
 
    static void Main()
    {
        someProcs += new Procedure(delegate { Console.WriteLine("test"); });
        AddProc();
        someProcs();
        Console.ReadKey();
    }
}

They can accept arguments just as normal methods can:

using System;
delegate void Procedure(string text);
 
class DelegateDemo3
{
    static Procedure someProcs = null;
 
    private static void AddProc()
    {
        int variable = 100;
 
        someProcs += new Procedure(delegate(string text)
            {
               Console.WriteLine(text + ", " + variable.ToString());
            });
    }
 
    static void Main()
    {
        someProcs += new Procedure(delegate(string text) { Console.WriteLine(text); });
        AddProc();
        someProcs("testing");
        Console.ReadKey();
    }
}

The output is:

testing
testing, 100

[edit] Lambda Expressions

Lambda expressions are a clearer way to achieve the same thing as an anonymous delegate. Its form is:

(type1 arg1, type2 arg2, ...) => expression

This is equivalent to:

delegate(type1 arg1, type2 arg2, ...)
{
    return expression;
}

If there is only one argument, the parentheses can be omitted. The type names can also be omitted to let the compiler infer the types from the context. In the following example, str is a string and the return type is a int:

Func<string, int> myFunc = str => int.Parse(str);

This is equivalent to:

Func<string, int> myFunc = delegate(string str)
{
    return int.Parse(str);
};

[edit] Events

An event is a special kind of delegate that facilitates event-driven programming. Events are class members which cannot be called outside of the class regardless of its access specifier. So, for example, an event declared to be public would allow other classes the use of += and -= on the event, but firing the event (i.e. invoking the delegate) is only allowed in the class containing the event. A simple example:

delegate void ButtonClickedHandler();

class Button
{
    public event ButtonClickedHandler ButtonClicked;
				
    public void SimulateClick()
    {
         if (ButtonClicked != null)
         {
              ButtonClicked();
         }
    }

    ...

}

A method in another class can then subscribe to the event by adding one of its methods to the event delegate:

Button b = new Button();
b.ButtonClicked += MyHandler;

Even though the event is declared public, it cannot be directly fired anywhere except in the class containing the event.