Beginner's Guide to Delegates, Funcs and Actions in C#
In this tutorial, I'll be giving a short introduction to delegates in C#.
So, what's a delegate?
In the same fashion that a class
is a reference type that holds references to objects, delegates
are also reference types, except they hold references to other methods
.
To put it even more plainly, classes are to objects what delegates are to methods.
When you look at it from that perspective, they're actually not that different, both classes and delegates are declared in similar fashion and both of them are reference types.
Types of delegates
There's a number of different types of delegates that we will be covering:
- The
Delegate
type - The
Action
type - The
Func
type
The Delegate type
Declaration
The delegate
type is declared by using the type name delegate, followed by the return type, name of the delegate and its input parameters.
// Structure of a delegate
delegate <return.type> <name> (<type.parameter>)
// Example
delegate string Foo (int value);
To reference a method to a delegate, the signature of both the delegate and method must match completely so:
- Return types must be the same.
- Input parameters must also be the same (and in matching order).
Example
Below we're referencing the Bar
method to the Foo
delegate.
void Main()
{
Foo fooExample = Bar;
string time = fooExample(DateTime.UtcNow);
Console.WriteLine (time);
}
public delegate string Foo (DateTime time);
public string Bar(DateTime value)
{
return value.ToString("t");
}
Example 2 - Multicasting
Multiple delegate
objects can be combined into a single instance, and it's the combination of delegates under that instance that becomes a Multicast delegate
.
When the multicast delegates are invoked, they iterate through their list of delegates and invoke them in order.
Carrying on from the example above.
void Main()
{
var now = DateTime.UtcNow;
Foo fooTime,
fooDate,
fooMulticast;
fooTime = BarTime;
fooDate = BarDate;
fooMulticast = fooTime + fooDate;
Console.Write("FooTime: \n----\n");
fooTime(now);
Console.WriteLine ();
/* - OUTPUT
FooTime:
----
04:23
*/
Console.Write("FooDate: \n----\n");
fooDate(now);
Console.WriteLine ();
/* - OUTPUT
FooDate:
----
21/04/2016
*/
Console.Write("FooMulticast: \n----\n");
fooMulticast(now);
/* - OUTPUT
FooMulticast:
----
04:23
21/04/2016
*/
}
public delegate string Foo (DateTime time);
public string BarTime(DateTime value)
{
var str = value.ToString("t");
Console.WriteLine ("{0}",str);
return str;
}
public string BarDate(DateTime value)
{
var str = value.ToString("dd/MM/yyyy");
Console.WriteLine ("{0}",str);
return str;
}
The Action delegate
The Action Delegate is a delegate which has a return type of void. The parameters of the action
delegate are set using type parameters.
void Example()
{
// Named method
this.NamedActionDelegate = NamedMethod;
this.NamedActionDelegate.Invoke("Hi", 5);
// Output > Named said: Hi 5
// Anonymous Function > Lambda
this.AnonymousActionDelegate.Invoke("Foooo", 106);
// Output > Anonymous said: Foooo 106
}
public Action<string, int> NamedActionDelegate { get; set; }
public Action<string, int> AnonymousActionDelegate = (text, digit) => Console.WriteLine ("Anonymous said: {0} {1}", text, digit);
public void NamedMethod(string text, int digit)
{
Console.WriteLine ("Named said: {0} {1}", text, digit);
}
The Func delegate
The Func Delegate is similar to the Action Delegate, the difference being that Func can never return void
, it will always require at least one type argument. As mentioned earlier, the type argument specified last dictates the return type of the delegate.
void Example()
{
// Named method
this.NamedFuncDelegate = NamedMethod;
string namedResult = this.NamedFuncDelegate.Invoke(5);
Console.WriteLine (namedResult);
// Output > Named said: 5
// Anonymous Function > Lambda
string anonyResult = this.AnonymousFuncDelegate.Invoke(106);
Console.WriteLine (anonyResult);
// Output > Anonymous said: 106
}
public Func<int, string> NamedFuncDelegate { get; set; }
public Func<int, string> AnonymousFuncDelegate = (digit) => { return string.Format("Anonymous said: {0}", digit); };
public string NamedMethod(int digit)
{
return string.Format ("Named said: {0}", digit);
}
Hi, thank you for this nice website. Please add the fact that Action & Function can accept up to 16 Arguments
Also, a question:
Why the output is always 9 and not 0-8 for all 9 methods? If you can help with this one, please!
Cheers!
The issue is that you’re directly referencing the variable
i
, not the value ofi
.The value of
i
starts off at0
, but it’s assigned new values on each iteration and the old value is discarded. You may be thinking, “but an integer is a value type, wouldn’t it just reference the value of the integer”.Not exactly, Jon Skeet elaborates pretty well into the why’s.
To fix your issue, you need to declare a copy of
i
and use the copy instead ofi
. Here’s howThanks for clarifying that. Much appreciated.
This is wonderful! Thanks much. Could you elaborate on the difference between Action and Func types, please?
Action types are methods that have a return type of void.
Func types are methods that never return void.
Both can accept an arbtitrary number of parameters, the key difference between them is that one returns void, whilst the other always has a return type and can never return void.
Thanks much!