Home > .Net / C# > Delegates and Events in C#

Delegates and Events in C#

Article overview

In this article series, I’m going to review the basic features and techniques used behind the Language Integrated Query (LINQ), including :

  • Delegates
  • Events (which are not directly related to LINQ, but are very closely related to delegates)
  • Lambda expressions (Anonymous functions)
  • Extension methods

At the end, I’ll summarize them and, hopefully, you will possess a better understanding of how LINQ works. ;)

Related articles:

 


What is a delegate in C# ?

A delegate in C# is a class that holds pointers to one or more methods for later execution. It’s a wrapper that provides dynamic invocation on the stored method references, both synchronious and asynchronious, along with some related meta-information. In its simplest form, a delegate is just a pointer to a function.


Why were the delegates  introduced ?

The delegates in C# were introduced in order to allow the developers to use techniques from the functional programming world in the procedural environment of the .Net platform.

For example, the delegates can be assigned to a variable and passed as function parameters, effectively making them “first class citizens”. That way, the inner workings of the methods can be replaced “dynamically” in a very elegant and effective way like, for instance, in LINQ, which is based on anonymous functions, extension methods and delegates.

    template <class T> T Addition(T operand1, T operand2) {
	   T result;
 
	   result = operand1 + operand2;
 
	   return result;
   }

 

Delegate basics

The delegate in its simplest form

As I mentioned, the delegate is just a class like the one below :

    sealed class Operation: System.MulticastDelegate
    {
        public int Invoke(int x, int y);
        public IAsyncResult BeginInvoke(int x, int y, AsyncCallback cb, object state);
        public int EndInvoke(IAsyncResult result);
    }

This forms the very basic implementation of a delegate that derives from System.MulticastDelegate and System.Delegate, respectively. The method Invoke() calls the actual stored method, which in this case should be declared with two parameters of type integer. The other two methods – BeginInvoke() and EndInvoke() – are used in an asynchronous scenarios, when you need to execute the stored function in a different thread than the calling one.


The delegate shortcut

Luckily, we are not required to manually write the declaration and definition of the class shown above. The following declaration will automatically generate the code for us behind the scenes :

public delegate string MyDelegate(bool a, bool b, bool c);

For example :

    namespace Test_ConsoleApp
    {

        public delegate int OperationDelegate(int param1, int param2);

        public class Operations
        {
            public int Addition(int op1, int op2)
            {
                return op1 + op2;
            }
        }

        public class Program
        {
            static void Main(string[] args)
            {
                Operations operationsInstance = new Operations();

                OperationDelegate pointer = new OperationDelegate(operationsInstance.Addition);

                Console.WriteLine(operationsInstance.Addition(1, 2));
                Console.WriteLine(pointer(1, 2));

                Console.ReadLine();
            }

        }
    }

If you explore the intellisense output on  the delegate, you’ll notice that it contains a number of additional methods.

delegates-intellisense

 

Using the method group conversion feature

Take a look on the following line :

OperationDelegate pointer = new OperationDelegate(operationsInstance.Addition);

Instead of instantiating the delegate class using the new keyword, we can use a feature called Method Group Conversion to directly assign the method to the delegate pointer. For instance :

OperationDelegate pointer = operationsInstance.Addition;

 

Using the multicast feature

Remember when I said that delegates store pointers to one or more methods ? Storing and calling multiple methods is called multicasting and can be achieved the following way :

            OperationDelegate pointer = new OperationDelegate(operationsInstance.Addition);

            pointer += operationsInstance.Addition;
            pointer += operationsInstance.Subsctraction;
            pointer += operationsInstance.SomeOtherCoolOperation;

            Console.WriteLine(pointer(1, 2));

When you invoke the delegate implicitly or explicitly, all the stored methods will get invoked. The snippet above uses operator overloading, which is a really cool feature that originates from C++.

 

Using the predefined delegates

The truth is that you rarely want or need to define any delegates yourself. There is a group of predefined generic delegates which you can use when you need a rather standard implementation :

   public delegate TResult Func<TResult>();
   public delegate TResult Func<T1, TResult>(T1 arg1);
   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);

   public delegate void Action();
   public delegate void Action<T1>(T1 arg1);
   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);

   public delegate int Comparison<T>(T x, T y);
   public delegate TOutput Converter<TInput, TOutput>(TInput input);
   public delegate bool Predicate<T>(T obj);
   public delegate void EventHandler<TEventArgs>(Object sender, TEventArgs e) where TEventArgs : EventArgs;

Some of them have similar declarations, but they are semantically different. Namely, if you want to point to a predicate, you’ll use the Predicate<T> class, even if there is a Func<T1, Result>  that takes practically the same arguments.

 

Events basics

What are the events in C Sharp ?

The events in C# are in fact simple wrappers around the delegates, which provide few convenient features that are really just a syntactic sugar. Consider the following example :

namespace Test_ConsoleApp
{

    public delegate void OperationInvoked(string message);

    public class Operations
    {
        private OperationInvoked callback = null;

        public void RegisterEvent(OperationInvoked function)
        {
            this.callback += function;
        }

        public void UnRegisterEvent(OperationInvoked function)
        {
            this.callback += function;
        }

        public int Addition(int op1, int op2)
        {
            this.callback("Addition performed.");

            return op1 + op2;
        }

        public int Subsctraction(int op1, int op2)
        {
            this.callback("Substraction performed.");

            return op1 - op2;
        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            Operations operationsInstance = new Operations();
            operationsInstance.RegisterEvent(Program.PrintMessage);

            operationsInstance.Addition(1, 2);
            operationsInstance.Subsctraction(1, 2);

            Console.ReadLine();
        }

        public static void PrintMessage(string message)
        {
            Console.WriteLine(message);
        }

    }
}

We have the Operations class that performs some business logic and that we want to observe (which is basically an implementation of the Observer pattern using the latest .Net features). We see the delegate member, which is declared as private and, we see a pair of accessors – a getter and a setter.

The whole idea is, of course, to provide an appropriate encapsulation. We don’t want to give exclusive access to our delegate, we don’t want anyone to be able to override the delegate’s already established list of methods. That’s why we declare it as private, and that’s why we provide accessor methods


So, what are the events after all ?

The events in C Sharp generate some of the code for you and provide few more minor conveniences. The following declaration

public event OperationInvoked operationPerformedEvent;

will actually generate

  • A private declaration of a delegate of the specified class
  • Accessors (a getter and a settor), which prohibits the user from directly accessing the delegate and it’s methods


In addition to that, the events provide the following conveniences :

  • An event can be used in an interface declaration, while a delegate can not
  • An event can only be invoked from the class in which it is declared
  • The event has to be declared in a specific way


Using events, our example becomes the following :

namespace Test_ConsoleApp
{
    public delegate void OperationInvoked(string message);

    public class Operations
    {
        public event OperationInvoked callback;

        public int Addition(int op1, int op2)
        {
            this.callback("Addition performed.");

            return op1 + op2;
        }

        public int Subsctraction(int op1, int op2)
        {
            this.callback("Substraction performed.");

            return op1 - op2;
        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            Operations operationsInstance = new Operations();
            operationsInstance.callback += Program.PrintMessage;

            operationsInstance.Addition(1, 2);
            operationsInstance.Subsctraction(1, 2);

            Console.ReadLine();
        }

        public static void PrintMessage(string message)
        {
            Console.WriteLine(message);
        }

    }
}

Notice that if we attempt to assign a new value to the callback

operationsInstance.callback = Program.PrintMessage;

a compile-time error will be thrown. In fact, the IDE itself will warn us before that.


Using the proper convention for declaring events

As I said in the previous section, the event has to be declared in a specific way. Or at least it’s a good practice to define in that way, using the following pattern :

public delegate void OperationInvoked(object sender, OperationArgs e);

You see the first parameter which is of type System.Object, and the second parameter, which is in fact a custom type that derives from System.EventArgs

    public class OperationEventArgs : EventArgs
    {
        public readonly string _message;
        public OperationEventArgs(string message)
        {
            _message = message;
        }
    }

If you take a look on all the standard Microsoft event implementations, you’ll notice exactly this pattern. It decouples the event declaration from the actual parameters passed, making them more orthogonal.


Conclusion

Pointing to methods, using them as variables and passing them through functions is in fact a very powerful technique. Although I would not use a pure functional programming language for everyday use, I do admire the benefits that come from this hybrid approach.

In this article, I’ve made a brief overview of the delegates in C#, which are critical for understanding the inner workings of LINQ. In my next articles on the topic, I’ll talk about Anonymous functions and Extension methods.

LINQ internals (Part 2): Lambda expressions



Like the article ? Share it ! ;)


  1. No comments yet.
  1. No trackbacks yet.


Copyright © Developing the future 2013. Licensed under the CC> BY-NC-ND 3.0 Creative Commons license.       
Audi R8 wallpapers Sony Xperia Z4 Tablet WiFi