Thursday, February 25, 2010

Why use a delegate in C#

There are a lot of articles on the internet on how to use delegates in C#. Normally something along the lines of “They are type safe versions of c/c++ function pointers”. This explanation is correct but means nothing if you have no experience in C!

So here is an explanation of why one would use a delegate. (They are used in events too but let’s leave that for now).

Remember one of the key principals in OOP is CHANGE. So things change all the time, the less areas of code we have to change the less likely bugs will appear

Here is the issue without using a delegate

An error occurs in our program and we want to log it somewhere. At the start of the project we have a static class Logger that has one method


public static class Logger

{

public static void WriteLogToDatabase(string textToLog)

{

Console.WriteLine("Writing to database the text " + textToLog);

}

}

From the main program we call the method like

class Program

{

static void Main(string[] args)

{

Logger.WriteLogToDatabase("An error has occured again");

}

}

All well and good we have deployed the project and it works great

We have decided we need to write errors to XML as well. So now you have to change the class Logger (which we know works but will have to be tested again because we have altered it) to include the new method and change main program to call it, now we have:

public static class Logger

{

public static void WriteLogToDatabase(string textToLog)

{

Console.WriteLine("Writing to database the text " + textToLog);

}

public static void WriteLogToXML(string textToLog)

{

Console.WriteLine("Writing to XML the text " + textToLog);

}

}

From the main program we call the method like:

class Program

{

static void Main(string[] args)

{

Logger.WriteLogToDatabase("An error has occured again");

Logger.WriteLogToXML("An error has occured again");

}

}

Now we have only 2 methods and you have to alter the code in 2 places main.cs and Logger.cs. What would happen if you had to add more methods e.g. WriteLogToMobile, WriteLogToPrinter , WriteLogToSomeFutureDeviceThatHasNotBeenInvented, another 50 methods? Changing the code over and over, more bugs, more places to change code.

Now it gets worse, another developer has created some more logging methods in another class that you want to call in your Logger.cs class. Time to change the code yet again, pasting new code into your logger class.


A solution with delegates


What you could do is change your Logger class to contain a method that accepts a delegate as a parameter. So the new method gets a pointer to a method we want to use. So Logger.cs doesn’t care what the method is, as long as it follows the same signature of the delegate. That means Logger.cs never has to be changed, retested or redeployed.

public static class Logger

{

public static void WriteLog(WriteLogDelegate writeLogDelegate, string textToLog)

{

Console.WriteLine(writeLogDelegate() + textToLog);

}

}

Also you can add as many new methods in different classes as you like, that way you don’t even have to change your original methods. So I have:

public static class SomeLoggingMethods

{

public static string WriteLogToDatabase()

{

// code to write to database etc

return("Writing to database the text ");

}

public static string WriteLogToXML()

{

// code to write to xml etc

return("Writing to XML the text ");

}

}

And then a new programmer adds this:

public static class SomeOtherLoggingMethodsSomeoneElseHasWritten

{

public static string WriteLogToCoolNewDevice()

{

// code to write to database etc

return ("Writing to Cool New Device the text ");

}

}

The only changes to our original is in main.cs. The class Logger.cs is encapsulated and does nto need any editing to add new features to it.

static void Main(string[] args)

{

WriteLogDelegate writeLog = new WriteLogDelegate(SomeLoggingMethods.WriteLogToDatabase);

Logger.WriteLog(writeLog, " An error");

writeLog = new WriteLogDelegate(SomeOtherLoggingMethodsSomeoneElseHasWritten.WriteLogToCoolNewDevice);

Logger.WriteLog(writeLog, " An error");

}


Delegates are used for all sorts of reasons and this is just one of them.

No comments:

Post a Comment