Delegates in the Common Type System
The runtime supports reference types called delegates that serve a purpose similar to that of function pointers in C++. Unlike function pointers, delegates are secure, verifiable, and type safe. A delegate type can represent any method with a compatible signature. While function pointers can only represent static functions, a delegate can represent both static and instance methods. Delegates are used for event handlers and callback functions in the .NET Framework.
The common language runtime does not support serialization of global methods, so delegates cannot be used to execute global methods in other application domains.
All delegates inherit from MulticastDelegate, which inherits from Delegate. The C#, Visual Basic, and C++ languages do not allow inheritance from these types, instead providing keywords for declaring delegates.
In addition to inherited methods, the common language runtime provides two special methods for delegate types: BeginInvoke and EndInvoke. For more information on these methods, see Calling Synchronous Methods Asynchronously.
Because delegates inherit from MulticastDelegate, a delegate has an invocation list, which is a list of methods that the delegate represents and that are executed when the delegate is invoked. All methods in the list receive the arguments supplied when the delegate is invoked.
The return value is not defined for a delegate that has more than one method in its invocation list, even if the delegate has a return type.
Creating and Using Delegates
In many cases, such as callback methods, a delegate represents only one method, and the only actions you need to take are creating the delegate and invoking it.
For delegates that represent multiple methods, the .NET Framework provides methods of the Delegate and MulticastDelegate delegate classes to support operations such as adding a method to a delegate's invocation list (the Delegate.Combine method), removing a method (the Delegate.Remove method), and getting the invocation list (the Delegate.GetInvocationList method).
It is not necessary to use these methods for event-handler delegates in C#, C++, and Visual Basic, as these languages provide syntax for adding and removing event handlers.
Closed Static Delegates and Open Instance Delegates
Delegates can represent static (Shared in Visual Basic) or instance methods. Usually when a delegate represents an instance method, the instance is bound to the delegate along with the method. For example, an event-handler delegate might have three instance methods in its invocation list, each with a reference to the object the method belongs to.
In the .NET Framework version 2.0, it is also possible to create an open delegate for an instance method. An instance method has an implicit instance parameter (represented by this in C# or Me in Visual Basic), and it can be represented by a delegate type that exposes this hidden parameter. That is, the delegate type must have an extra parameter at the beginning of its formal parameter list, of the same type as the class the method belongs to. The converse of this scenario is also supported, so that it is possible to bind the first argument of a static method.
The creation of open instance and closed static delegates is not directly supported by Visual Basic, C++, or C# for delegate constructors. Instead, use one of the Delegate.CreateDelegate method overloads that specifies MethodInfo objects, such as Delegate.CreateDelegate(Type, Object, MethodInfo, Boolean).
Relaxed Rules for Delegate Binding
In the .NET Framework version 2.0, the parameter types and return type of a delegate must be compatible with the parameter types and return type of the method the delegate represents; the types do not have to match exactly.
In the .NET Framework versions 1.0 and 1.1, the types must match exactly.
A parameter of a delegate is compatible with the corresponding parameter of a method if the type of the delegate parameter is more restrictive than the type of the method parameter, because this guarantees that an argument passed to the delegate can be passed safely to the method.
Similarly, the return type of a delegate is compatible with the return type of a method if the return type of the method is more restrictive than the return type of the delegate, because this guarantees that the return value of the method can be cast safely to the return type of the delegate.
For more information and example code, see Delegate.CreateDelegate(Type, Object, MethodInfo).
Delegates and Asynchronous Method Calls
Every delegate has a BeginInvoke method that allows you to call the delegate asynchronously, and an EndInvoke method that cleans up resources afterward. These methods are generated automatically for each delegate type. When a delegate is invoked by using the BeginInvoke method, the method the delegate represents is executed on a thread belonging to the ThreadPool.
For more information and example code, see Asynchronous Programming Using Delegates.