MorkaLork Development

Interesting stuff I've picked up over the years...

Using delegates

2009-11-26 16:45:59 | 472 views | C# delegate delegates

Delegates


What is this article about?



This article is about using delegates in C#. A delegate is a reference to a method. Whereas objects can easily be sent as parameters into methods, constructor or whatever, methods are a bit more tricky. But every once in a while you might feel the need to send a method as a parameter to another method, and that's when you'll need delegates.

This article will show you how to use delegates. One of the many problems with the tutorials on the net is that they only show what a delegate is, not why a delegate is. I will try to fill this void and give you a good example.

This article will also show the close link between delegates and lambda expressions.


Delegate syntax



A delegate is declared inside a class, not a method, and the syntax looks like this:

[Access modifier] delegate [Return type] [Name] [Parameter]

Ex:
Public delegate void MyDelegate();
or
Public delegate string MyStringDelegate(string s);

A delegate can then be replaced with any method having the same method header as the delegate. That means that the method must have the same return type and the same parameters. If the delegates return type is void and parameter is empty then the method can not have any parameters and must be void.


Our example


Before peeking at the example I will explain what we are going to create and why we create it this way.
We will make a filter for a generic list. I assume that if you read about delegates you are familiar with generic collections.
The application will have a list of people, made up of Person objects. We will then have a method to output the people to our console after having being passed through one of our filters.
The filters are the key to this application since our outputting mechanism will take a filter method as a parameter. This is when we will use a delegate.

First, we have a Person class:



/// <summary>
/// A class to define a person
/// </summary>
public class Person {
public string Name { get; set; }
public int Age { get; set; }
}


This class will only represent a person, that's it.

Our main class, Program will have a delegate that we call FilterDelegate, and it will look like this:



public delegate bool FilterDelegate(Person p);


It will represent a filtering method that returns either true or false depending on the filter. At this moment, we don't know anything about the filter except that it require a Person object and that it returns a bool.

We will have 3 filtering methods. They will look like this:



static bool IsChild(Person p) {
return p.Age <= 18;
}

static bool IsAdult(Person p) {
return p.Age >= 18;
}

static bool IsSenior(Person p) {
return p.Age >= 65;
}


They are quite self-explanatory; IsChild checks if a Person object is a child, IsAdult check is the Person object is an adult and IsSenior check for senior citizens.
Notice that they all have the same silhouette as the delegate; they return a bool and they require a Person object.

Now for the reason we are using delegates. We have a method, called DisplayPeople that outputs people from a list. It requires a title, a list of Person objects and a filter:



static void DisplayPeople(string title, List<Person> people, FilterDelegate filter) {
Console.WriteLine(title);
foreach (Person p in people) {
if (filter(p)) {
Console.WriteLine("{0}, {1} years old", p.Name, p.Age);
}
}
Console.Write("\n\n");
}


As we can see in this code, we traverse through the list of Person objects. For every Person in the list we apply the filter sent in by the caller. If the filter returns true, we output the Person objects property name.
If we for example enter the IsAdult filter, it will check if the age of the Person in question is 18 or above. If so, the Person objects name property will be outputted.

So, how do we call such a method?



static void Main(string[] args) {
//Create 4 Person objects
Person p1 = new Person() { Name = "John", Age = 41 };
Person p2 = new Person() { Name = "Jane", Age = 69 };
Person p3 = new Person() { Name = "Jake", Age = 12 };
Person p4 = new Person() { Name = "Jessie", Age = 25 };

//Create a list of Person objects and fill it
List<Person> people = new List<Person>() { p1, p2, p3, p4 };

DisplayPeople("Children:", people, IsChild);
DisplayPeople("Adults:", people, IsAdult);
DisplayPeople("Seniors:", people, IsSenior);

Console.Read();
}


Output:
Children:
Jake, 12 years old


Adults:
John, 41 years old
Jane, 69 years old
Jessie, 25 years old


Seniors:
Jane, 69 years old



As we can see, we create 4 Person objects and enter them into a List of Person. We then run the DisplayPeople method three times in order to output the categories Children, Adults and Seniors using our filter that we created earlier.


What? Why!


The alternative to our solution would be to create three separate methods to parse through the list and output filtered people. We still need three methods, the filter methods, but we can reuse all code except for the boolean check which makes this a very effective way to code.


The full code




using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MyLibrary;

namespace DelegateApp {

/// <summary>
/// A class to define a person
/// </summary>
public class Person {
public string Name { get; set; }
public int Age { get; set; }
}

class Program {
//Our delegate
public delegate bool FilterDelegate(Person p);

static void Main(string[] args) {
//Create 4 Person objects
Person p1 = new Person() { Name = "John", Age = 41 };
Person p2 = new Person() { Name = "Jane", Age = 69 };
Person p3 = new Person() { Name = "Jake", Age = 12 };
Person p4 = new Person() { Name = "Jessie", Age = 25 };

//Create a list of Person objects and fill it
List<Person> people = new List<Person>() { p1, p2, p3, p4 };

DisplayPeople("Children:", people, IsChild);
DisplayPeople("Adults:", people, IsAdult);
DisplayPeople("Seniors:", people, IsSenior);

Console.Read();
}

/// <summary>
/// A method to filter out the people you need
/// </summary>
/// <param name="people">A list of people</param>
/// <param name="filter">A filter</param>
/// <returns>A filtered list</returns>
static void DisplayPeople(string title, List<Person> people, FilterDelegate filter) {
Console.WriteLine(title);
foreach (Person p in people) {
if (filter(p)) {
Console.WriteLine("{0}, {1} years old", p.Name, p.Age);
}
}
Console.Write("\n\n");
}

//==========FILTERS===================
static bool IsChild(Person p) {
return p.Age <= 18;
}

static bool IsAdult(Person p) {
return p.Age >= 18;
}

static bool IsSenior(Person p) {
return p.Age >= 65;
}
}
}



Lambda expressions


I won't get into lamdba expressions in depth but just show how to use lambda expressions and funcs.

A lamdba expression is the very next step after using delegates. Previously we created three filter methods which we used via a delegate in the DisplayPeople method argumenting that using that technique we saved a lot of coding. We can actually shorten it down even further by using lambda expressions.

When we use a delegate we send in a method which means we can either write a method or use an anonymous method. Using lamdba expressions we don't have do this. Lambda expressions are like shortened anonymous methods that can either be used directly or stored as a function.

Using the earlier example we will output the children, adults and seniors in a list of Person objects using lamdba expressions instead of delegates.



static void Main(string[] args) {
//Create 4 Person objects
Person p1 = new Person() { Name = "John", Age = 41 };
Person p2 = new Person() { Name = "Jane", Age = 69 };
Person p3 = new Person() { Name = "Jake", Age = 12 };
Person p4 = new Person() { Name = "Jessie", Age = 25 };

//Create a list of Person objects and fill it
List<Person> people = new List<Person>() { p1, p2, p3, p4 };

//Using lamdba expressions
DisplayPeople("Children:", people, n => n.Age <= 18);
DisplayPeople("Adults:", people, n => n.Age >= 18);
DisplayPeople("Seniors:", people, n => n.Age >= 65);

Console.Read();
}


This might look totally crazy to you, but it's not. What we do here is just the same as before, but now we don't need the three filter methods we built earlier.

This is how it all works:

n => n.Age <= 18
The first n is the parameter sent in to the parameter and n.Age <= 18 is what the function returns.
This can all be placed in a function which might help to explain it better.

A lambda function looks like this:
func<[parameter type],[return type]> Name = [Parameter object] => expression

A func like this can now be used as a delegate, so let's change our code as follows:



static void Main(string[] args) {
//Create funcs
Func<Person, bool> Children = n => n.Age <= 19;
Func<Person, bool> Adults = n => n.Age > 18;
Func<Person, bool> Seniors = n => n.Age >= 65;

//Create 4 Person objects
Person p1 = new Person() { Name = "John", Age = 41 };
Person p2 = new Person() { Name = "Jane", Age = 69 };
Person p3 = new Person() { Name = "Jake", Age = 12 };
Person p4 = new Person() { Name = "Jessie", Age = 25 };

//Create a list of Person objects and fill it
List<Person> people = new List<Person>() { p1, p2, p3, p4 };

//Using lamdba expressions
DisplayPeople("Children:", people, Children);
DisplayPeople("Adults:", people, Adults);
DisplayPeople("Seniors:", people, Seniors);

Console.Read();
}

/// <summary>
/// A method to filter out the people you need
/// </summary>
/// <param name="people">A list of people</param>
/// <param name="filter">A filter</param>
/// <returns>A filtered list</returns>
static void DisplayPeople(string title, List<Person> people, Func<Person, bool> filter) {
Console.WriteLine(title);
foreach (Person p in people) {
if (filter(p)) {
Console.WriteLine("{0}, {1} years old", p.Name, p.Age);
}
}
Console.Write("\n\n");
}


Notice that we have to change the parameter in the DisplayPeople method as well. But the output is exactly the same.

Let's compare the delegate to the function:

public delegate bool FilterDeletage(Person p);
func<Person, bool> IsAdult = n => n.Age > 18

The delegate is somewhat self-explanatory as it looks sort of like a method but with a semicolon at the end instead of curly brackets and an implementation.
The func however has a very different syntax and might be a bit hard to comprehend at start, so I'll let a picture explain:

The difference between delegates and lambda

The bool is the return type, and the n.Age > 18 is the return value. Since we have bool as return type we have to have an boolean expression. If we had returned a string we might've had n => n.Name only since whatever comes after the => sign is the return value.


Hope this has shed some light on the matter of delegates and lambda expressions.


Article comments

Feel free to comment this article using a facebook profile.

I'm using facebook accounts for identification since even akismet couldn't handle all the spam I receive every day.