A delegate is a reference to a method. You can call this method using your delegate, or you can point the delegate to a new method.
I think the syntax of delegates makes it slightly inaccessible for developers new to C# to understand, so I want to help people that are in the same position that I was in myself not so long ago.
There is a great summary of delegates on StackOverflow:
By defining a delegate, you are saying to the user of your class: "Please feel free to put any method that match this signature here and it will be called each time my delegate is called"
- Benoit Vidis
http://stackoverflow.com/a/2019542/568371
JavaScript Delegates
If you don't know JavaScript, please skip this section, but I want to give those who do know a bit of JavaScript a leap in the right direction. In JavaScript, delegate style syntax is dead easy, because we don't need to worry about specifying parameter and return types. Here's what a delegate would look like in JavaScript:
function someFunction(someParameter) {
return someParameter + 1;
};
var myDelegate = someFunction; //a delegate is a reference to a method
alert(myDelegate(1)); //calling our delegate to alert "2"
myDelegate = function(someParameter) { //you can repoint the delegate to some other method
return someParameter + 2;
};
alert(myDelegate(1)); //calling our delegate to alert "3"
Now let's see what it looks like in C#:
C# Delegates
In C# we need to be more specific about the Type
s that come into the delegate as parameters and that go out as a return type.
Let's see a silly example. We have a list of names and we want to filter it based on different criteria. OK, you might not use delegates for this example in the real world, but it shows how you can use them.
internal class Roster
{
private List<string> _names = new List<string> { "tim", "tom", "harry" };
public List<string> GetFilteredNames(FilterByName filter) //our method is expecting another method as a parameter that matches the delegate signature
{
return _names.Where(name => filter(name)).ToList();
}
public delegate bool FilterByName(string name); //we want to filter the names so the delegate should return a true value if the name passed in is wanted
}
class Program
{
static void Main(string[] args)
{
var roster = new Roster();
Console.WriteLine(string.Join(", ", roster.GetFilteredNames(IsTim)));
Console.WriteLine(string.Join(", ", roster.GetFilteredNames(IsNotTim)));
Console.ReadLine();
}
static bool IsTim(string name)
{
return name == "tim";
}
static bool IsNotTim(string name)
{
return name != "tim";
}
}
LINQ Queries
The .Where
syntax might be more familiar to you if you are a fan of LINQ. You might have written the same thing using Func
and anonymous functions. Let's look at the same thing using this shorter syntax:
internal class Roster
{
private List<string> _names = new List<string> { "tim", "tom", "harry" };
public List<string> GetFilteredNames(Func<string, bool> filter) //the last type parameter of Func is always the return type. If there is no return type you can use Action<T>
{
return _names.Where(name => filter(name)).ToList();
}
}
Now I could write the program like this:
static void Main(string[] args)
{
var roster = new Roster();
Func<string, bool> myFilter = name => name == "tim";
Console.WriteLine(string.Join(", ", roster.GetFilteredNames(myFilter)));
myFilter = name => name != "tim";
Console.WriteLine(string.Join(", ", roster.GetFilteredNames(myFilter)));
Console.ReadLine();
}
This is because the LINQ syntax of name => name == "tim"
takes 1 string
parameter and returns a bool
, as defined in our Func<string, bool>
definition.
Events
You may have also seen code like this, used to send notifications across applications when specific actions happen:
public delegate void MyEventHandler(object sender, EventArgs e); //an event handler for this event will expect this same signature
public event MyEventHandler OnSomeEvent; //the event is using the above delegate
private void SomeFunction() {
if (OnSomeEvent != null) { //checks the event is being "listened" to (ie someone added a delegate to the event)
OnSomeEvent(this, new EventArgs()); //raise the event using all delegates registered
}
}
/*the lines at the start of this listing could be rewritten using newer, generic syntax*/
public event EventHandler<EventArgs> OnSomeEvent;
//this is because the EventHandler system type signature looks like this:
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
//the if statement could also be rewritten in C#6:
OnSomeEvent?.Invoke(this, new EventArgs());
When you understand what a delegate is, you realise an event is simply a collection of delegates matching the same signature that can be called when something happens elsewhere in the codebase. So you can listen to an event like this as follows:
public void DoSomething() {
var myEventRaisingObject = new EventRaisingObject();
myEventRaisingObject.OnSomeEvent += OnSomeEventRaised; //this is adding our delegate to the event handler
myEventRaisingObject.OnSomeEvent -= OnSomeEventRaised; //removes the delegate from the event handler
//you can equally write this anonymously:
myEventRaisingObject.OnSomeEvent += (object sender, EventArgs e) => DoSomethingElse();
}
private void OnSomeEventRaised(object sender, EventArgs e) {
DoSomethingElse();
}
Higher Order Functions
You can also use delegates as a return type from another method. That allows you to encapsulate the functionality of your delegate in a scope that might have some important data you want to use. Here's an example from our earlier program:
public Func<string, bool> GetFilterForName(string[] namesToInclude)
{
return name => namesToInclude.Contains(name);
}
//calling code
Console.WriteLine(string.Join(", ", roster.GetFilteredNames(roster.GetFilterForName(new[] { "tim", "harry" }))));
Conclusion
I hope this has helped your understanding of C# delegates. I have rarely found a valid use for them besides event handlers in production, but there are times they can come in handy.
If you are looking for a real world example, one recent project involved a mapping between two APIs, in which it was useful to define delegates to fetch each property from the first API and translate it into a format that the second API understood.