A delegate type is a type representing a particular method signature. An instance of this type refers to a particular method with a matching signature. Method parameters may have delegate types, and so this one method to be passed a reference to another method, which may then be invoked
In-built delegate types:
System namespace contains
Func<...,TResult> delegates, where the "..." represents between 0 and 16 generic type parameters (for 0 parameters,
Action is non-generic).
Func represents methods with a return type matching
Action represents methods without a return value (void). In both cases, the additional generic type parameters match, in order, the method parameters.
Predicate represents method with boolean return type, T is input parameter.
Custom delegate types
Named delegate types can be declared using the
Delegates can be invoked using the same syntax as methods: the name of the delegate instance, followed by parentheses containing any parameters.
Assigning to delegates
Delegates can be assigned to in the following ways:
- Assigning a named method
- Assigning an anonymous method using a lambda
- Assigning a named method using the
Multiple delegate objects can be assigned to one delegate instance by using the
+ operator. The
- operator can be used to remove a component delegate from another delegate.
Assigning a named method to a delegate
Named methods can be assigned to delegates with matching signatures:
Example.AddOne takes an
int and returns an
int, its signature matches the delegate
Example.AddOne can be directly assigned to
addOne because they have matching signatures.
Assigning to a delegate by lambda
Lambdas can be used to create anonymous methods to assign to a delegate:
Note that the explicit declaration of type is required when creating a variable this way:
Closure inside a delegate
Closures are inline anonymous methods that have the ability to use
Parent method variables and other anonymous methods which are defined in the parent's scope.
In essence, a closure is a block of code which can be executed at a later time, but which maintains the environment in which it was first created - i.e. it can still use the local variables etc of the method which created it, even after that method has finished executing. -- Jon Skeet
Example taken from Closures in .NET.
Combine Delegates (Multicast Delegates)
+ and subtraction
- operations can be used to combine delegate instances. The delegate contains a list of the assigned delegates.
In this example
d3 is a combination of
d2 delegates, so when called the program outputs both
Combining delegates with non void return types:
If a multicast delegate has a
nonvoid return type, the caller receives the return value
from the last method to be invoked. The preceding methods are still called, but their
return values are discarded.
t(2) will call first
Square and then
Cube. The return value of Square is discarded and return value of the last method i.e.
Cube is retained.
Declaring a delegate type
The following syntax creates a
delegate type with name
NumberInOutDelegate, representing a method which takes an
int and returns an
This can be used as follows:
example delegate instance is executed in the same way as the
Square method. A delegate instance literally acts as a delegate for the caller: the caller invokes the
delegate, and then the delegate calls the target method. This indirection decouples
the caller from the target method.
You can declare a generic delegate type, and in that case you may specify that the type is covariant (
out) or contravariant (
in) in some of the type arguments. For example:
Like other generic types, generic delegate types can have constraints, such as
where TFrom : struct, IConvertible where TTo : new().
Avoid co- and contravariance for delegate types that are meant to be used for multicast delegates, such as event handler types. This is because concatenation (
+) can fail if the run-time type is different from the compile-time type because of the variance. For example, avoid:
Instead, use an invariant generic type:
Also supported are delegates where some parameters are modified by
out, as in:
TryParser<decimal> example = decimal.TryParse;), or delegates where the last parameter has the
params modifier. Delegate types can have optional parameters (supply default values). Delegate types can use pointer types like
char* in their signatures or return types (use
unsafe keyword). A delegate type and its parameters can carry custom attributes.
.Equals() on a delegate compares by reference equality:
These rules also apply when doing
-= on a multicast delegate, for example when subscribing and unsubscribing from events.
Encapsulating transformations in funcs
In the spirit of clean coding, encapsulating checks and transformations like the one above as a Func can make your code easier to read and understand. While the above example is very simple, what if there were multiple DateTime properties each with their own differing validation rules and we wanted to check different combinations? Simple, one-line Funcs that each have established return logic can be both readable and reduce the apparent complexity of your code. Consider the below Func calls and imagine how much more code would be cluttering up the method:
Passing delegates as parameters
Delegates can be used as typed function pointers:
Safe invoke multicast delegate
Ever wanted to call a multicast delegate but you want the entire invokation list to be called even if an exception occurs in any in the chain. Then you are in luck, I have created an extension method that does just that, throwing an
AggregateException only after execution of the entire list completes:
Invoking directly, without
SaveInvoke, would only execute Target 2.
, Action and Predicate delegate types
The System namespace contains
Func<..., TResult> delegate types with between 0 and 15 generic parameters, returning type
The System namespace also contains
Action<...> delegate types with different number of generic parameters (from 0 to 16). It is similar to
Func<T1, .., Tn>, but it always returns
Predicate<T> is also a form of
Func but it will always return
bool. A predicate is a way of specifying a custom criteria. Depending on the value of the input and the logic defined within the predicate, it will return either
Predicate<T> therefore behaves in the same way as
Func<T, bool> and both can be initialized and used in the same way.
The choice of whether to use
Func<T, bool> is really a matter of opinion.
Predicate<T> is arguably more expressive of the author's intent, while
Func<T, bool> is likely to be familiar to a greater proportion of C# developers.
In addition to that, there are some cases where only one of the options is available, especially when interacting with another API. For example
Array<T> generally take
Predicate<T> for their methods, while most LINQ extensions only accept
Underlying references of named method delegates
When assigning named methods to delegates, they will refer to the same underlying object if:
They are the same instance method, on the same instance of a class
They are the same static method on a class