This article has completely been reviewed, thanks to many encouraging comments below.
Introduction
Delegates play a major role in C# (e.g. LINQ's lambda expressions), so it's a good idea to really understand them.
Luckily, you'll find a whole bunch of well written articles on CodeProject that explain delegates (see links at the end). But IMHO, most articles fail to include a good example. Either they are too simple or too complex.
In this article, I'll try to give a practical example, albeit not from the real coding world.
It's meant to give you a *feel* of how and when to use delegates.
Still, the code will execute!
Delegates Simplified Recap
All you C# buffs will excuse me if I give a quick recap of delegates, and oversimplify things while doing so.
First off, a delegate is a type, i.e. a class. Itβs a special class (you'll see why in a sec), but still, it's just a class.
That means that like any other class, in order to be used, it must be:
- declared and
- instantiated
This will result in an object. That object can then be:
These are the 3 main steps in the lifecycle of a delegate. I'll point them out in the code.
Episode 1 β Without Delegates: the MarketingDepartment and its Service Providers
Suppose you have a class, called MarketingDepartment
. It's job is to run advertising campaigns on new prospects (class CustomerAddress
).
If the budget is less then 10000, only Ballpens will be sent to the prospects. If we can afford more, we'll send coffee cups!
In order to do that, MarketingDepartment
has 3 different "service providers" : an AddressProvider
that will provide addresses, a BallpenCompany
that will send the ballpens, and a CoffeeCupCompany
that... well, you get the idea !
In an UML class diagram, it could look like this:
In code, we could write something like this:
class Program
{
static void Main(string[] args)
{
bool success = false;
MarketingDepartment MyDepartment = new MarketingDepartment();
success = MyDepartment.ExecuteNewCampaign(5000);
Console.WriteLine("The new marketing campaign has {0} !",
success == true ? "succeeded" : "failed");
}
}
public class CustomerAddress
{
}
The Program
class is the starting point. CustomerAddress
is the class we'll use to pass addresses of prospects.
The real shabang goes on in the MarketingDepartment
:
we'll fetch the adresses, and send those to the BallpenCompany
or the CoffeeCupCompany
, depending on the given budget.
public class MarketingDepartment
{
public bool ExecuteNewCampaign(decimal budget)
{
bool success = false;
AddressProvider MyAddressProvider = new AddressProvider();
List<CustomerAddress> ListOfAddresses =
MyAddressProvider.GetAddressesNewProspects();
if (budget < 10000)
{
BallpenCompany MyBallpenCompany = new BallpenCompany();
success = MyBallpenCompany.SendBallPens(ListOfAddresses);
}
else
{
CoffeeCupCompany MyCoffeeCupCompany = new CoffeeCupCompany();
success = MyCoffeeCupCompany.SendCoffeeCups(ListOfAddresses);
}
return success;
}
}
These are the service providers:
public class AddressProvider
{
public List<CustomerAddress> GetAddressesNewProspects()
{
List<CustomerAddress> ListOfAddresses = new List<CustomerAddress>();
return ListOfAddresses;
}
}
public class BallpenCompany
{
public bool SendBallPens(List<CustomerAddress> ListOfAddresses)
{
return true;
}
}
public class CoffeeCupCompany
{
public bool SendCoffeeCups(List<CustomerAddress> ListOfAddresses)
{
return true;
}
}
Episode 2 β Enter the Delegate: Tell the AddressProvider How to Proceed!
The Marketing people, smart as they are, have an idea to speed things up! They say: "Hey, we don't want to waste time getting the addresses back from the AddressProvider
and then send them to the BallpenCompany
or the CoffeeCupCompany
. Instead, it would save time if the AddressProvider
knows immediately what to do with the addresses." />
In short, this is the strategy to follow:
- A public delegate is declared, that will serve as the wrapper of the method that has to be invoked
- The
MarketingDepartment
will fill that wrapper with the method of its choice (SendBallpens
or SendCoffeeCups
), and pass the wrapper to AddressProvider
- The
AddressProvider
will accept the wrapper, and invoke it, without even knowing what concrete method is being executed
Here's how it would look like in code (I have cut out the bits that didn't change)
public delegate bool DoAfterGetAddresses(List<CustomerAddress> ListOfAddresses);
public class MarketingDepartment
{
public bool ExecuteNewCampaign(decimal budget)
{
bool success = false;
AddressProvider MyAddressProvider = new AddressProvider();
DoAfterGetAddresses ToDoAfterAddresses;
if (budget < 10000)
{
BallpenCompany MyBallpenCompany = new BallpenCompany();
ToDoAfterAddresses = MyBallpenCompany.SendBallPens;
}
else
{
CoffeeCupCompany MyCoffeeCupCompany = new CoffeeCupCompany();
ToDoAfterAddresses = MyCoffeeCupCompany.SendCoffeeCups;
}
success = MyAddressProvider.HandleCampaign(ToDoAfterAddresses);
return success;
}
}
public class AddressProvider
{
public bool HandleCampaign(DoAfterGetAddresses ToDoAfterAddresses)
{
bool success = false;
List<CustomerAddress> ListOfAddresses = GetAddressesNewProspects();
success = ToDoAfterAddresses(ListOfAddresses);
return success;
}
public List<CustomerAddress> GetAddressesNewProspects()
{
....
}
}
Conclusion
A delegate turns out to be a sort of an interface of a method of an object -- ANY method of ANY object that meets its signature.
The delegate object can then be passed around as a parameter, and invoked by the receiving object.
In UML, I don't know (yet) how a delegate is diagrammed, but this is my best shot (better suggestions are welcome!)
Episode 3 β Another Scenario: Delegates that Tell the MarketingDepartment How to Proceed!
Another scenario would be that the AddressProvider
has a connection with a BallpenCompany
and a CoffeeCupCompany
, as the UML class diagram shows.
Instead of exposing those companies to the MarketingDepartment
, the AdressProvider
makes 2 delegates available: one for sending ballpens, another for sending coffee cups.
Here's the UML:
And here's the code. Tip: have a look first at the AddressProvider
!
public class MarketingDepartment
{
public bool ExecuteNewCampaign(decimal budget)
{
bool success = false;
AddressProvider MyAddressProvider = new AddressProvider();
List<CustomerAddress> ListOfAddresses =
MyAddressProvider.GetAddressesNewProspects();
if (budget < 10000)
{
success = MyAddressProvider.MySendBallPens(ListOfAddresses);
}
else
{
success = MyAddressProvider.MySendCoffeeCups(ListOfAddresses);
}
return success;
}
}
public delegate bool SendBallPens(List<CustomerAddress>ListOfAddresses);
public delegate bool SendCoffeeCups(List<CustomerAddress>ListOfAddresses);
public class AddressProvider
{
private BallpenCompany MyBallpenCompany;
private CoffeeCupCompany MyCoffeeCupCompany;
public SendBallPens MySendBallPens;
public SendCoffeeCups MySendCoffeeCups;
public AddressProvider()
{
MyBallpenCompany = new BallpenCompany();
MyCoffeeCupCompany = new CoffeeCupCompany();
MySendBallPens = MyBallpenCompany.SendBallPens;
MySendCoffeeCups = MyCoffeeCupCompany.SendCoffeeCups;
}
public List<CustomerAddress> GetAddressesNewProspects()
{
....
}
}
Conclusion
Delegates are a cornerstone for a significant portion of the C# language, especially the event-framework.
Yet, they have a right on their own to be used in code.
So, it's worth spending some time trying to understand them as completely as possible.
Although there are many well-written articles available (see links below), most of them in my humble opinion lack a practical example that is neither too simple nor too complex.
This article tried to fill that gap. I hope it succeeded somewhat in achieving that goal.
Links
These are articles I learned a lot from. Some of them are even fun to read!