The evolution of the delegate

As the .NET framework evolved, there were some great reasons for a delegate to be declared and used in different ways / forms to make coding simpler. I’ve also found several developers quite accustomed to using lambda functions with their LINQ queries, but had no idea that a lambda function is a delegate. If you just said: “Lambda is a delegate?!?” or are interested as to how we got to the lambda function from delegate, then this article is for you.

Before we dive right in, if you’re unsure how delegates work, I’d suggest reading this article first: Simply explanation to Delegates and Events

In .NET 1, the delegate looked like this:

delegate void LogSomeText(string text);

Whenever we wanted to add a method to the delegate we had to create a new method, pass it into the delegate and then do with the delegate as we needed. Sounds easy enough… No change needed, right?

Consider the following:

public class DelegateTest
{
  public delegate void LogSomeText(string text);
  private void ConsoleLogger(string text)
  {
    Console.WriteLine("Logging: " + text);
  }

  public void Go()
  {
    SomeMethod(ConsoleLogger);
  }

  public void SomeMethod(LogSomeText logger)
  {
    logger("This is my text to be logged");
  }
}

The code above has a method SomeMethod, which expects a delegate. Line 11 we want to call SomeMethod, and so we had to create a new methods called ConsoleLogger just to be able to pass something in at Line 11. Doesn’t sound too bad??

ConsoleLogger is literally just 1 line of code. We had to think of a method name, think of a parameter name and create the method. If we didn’t want the method exposed to the outside we would then make it private. Imagine we had 20 different places where we call SomeMethod, all doing something slightly different to ConsoleLogger. We’d have to create 20 methods, all with unique names, just to pass a “unit of work” to SomeMethod. That’s usually when we end up with methods like this: MyMethod1, MyMethod2 etc.

Ok, so the above is slightly exaggerated, but I hope you see where I’m going. Enter anonymous delegates…

public class DelegateTest
{
  public delegate void LogSomeText(string text);
  public void Go()
  {
    SomeMethod(delegate (string text)
    {
      Console.WriteLine("Logging: " + text);
    });
  }

  public void SomeMethod(LogSomeText logger)
  {
    logger("This is my text to be logged");
  }
}

As you can see, we’ve now removed the method ConsoleLogger completely and in the parameter at line 6 we’ve introduced what’s called an anonymous delegate. No we don’t need to create a new method for every time a delegate is expected and we don’t need to worry about create lots of private methods called Method1, Method2 etc. That’s the beauty of an anonymous method (it has no name).

Can the code get any cleaner? Well yes it can. Let’s introduce Action and Func generic delegates. No doubt that if you work a lot with LINQ you would have noticed that the LINQ extension methods take Action or Func overloads as parameters. First time I encountered them, I was rather intimidated. But don’t fear, they’re very easy to understand and use.

Action and Func are generic delegates. They were introduced to allow us to expect a delegates with different signatures, without having to declare a new delegate every-time.

Remember this, it will make sense later:

  • Action is a delegate with no return type (void).
  • Func is a delegate with a return type.

Consider this exaggerated scenario:

public delegate long Add2Nums(int num1, int num2);
public delegate long Add3Nums(int num1, int num2, int num3);
public delegate long Add4Nums(int num1, int num2, int num3, int num4);
public delegate long Add5Nums(int num1, int num2, int num3, int num4, int num5);

public void LotsOfAdding(
      Add2Nums calc1,
      Add3Nums calc2,
      Add4Nums calc3,
      Add5Nums calc4)
{
  var ans1 = calc1(10, 20);
  var ans2 = calc2(30, 15, 20);
  var ans3 = calc3(2, 44, 20, 30);
  var ans4 = calc4(10, 20, 30, 40, 50);
}

You can see that everytime we need a different overload, we have to create a new delegate and name it something else. This becomes quite redundant and error prone.

To simplify the above code, we can replace the delegates with either Action or Func. in our case, they all have return types of long so they will use the Func delegate.

public void LotsOfAdding(
      Func<int, int, long> calc1,
      Func<int, int, int, long> calc2,
      Func<int, int, int, int, long> calc3,
      Func<int, int, int, int, int, long> calc4)
{
  var ans1 = calc1(10, 20);
  var ans2 = calc2(30, 15, 20);
  var ans3 = calc3(2, 44, 20, 30);
  var ans4 = calc4(10, 20, 30, 40, 50);
}

You can see that we did not need to create a new delegate each time, but simply use the Func delegate. The last generic type specified with the Func delegate, is the return type (in this case long).

So basically, this:

Func<int, int, long>

would be as if you had declared this:

public delegate long Add2Nums(int num1, int num2)

So let’s get back to our original piece of code and see if we can use the Action and Func delegates:

public class DelegateTest
{
  public void Go()
  {
    SomeMethod(delegate (string text)
    {
      Console.WriteLine("Logging: " + text);
    });
  }

  public void SomeMethod(Action<string> logger)
  {
    logger("This is my text to be logged");
  }
}

You can see that we’ve taken out the delegate declaration and in line 11, replaced the parameter with Action<string>. Remember that Action is for no return types (void). And the string generic type is for the first parameter.

This is already looking a lot cleaner in my opinion. Let’s introduce the last change, lamdba. A Lambda expression, simply put is a shortened version of an anonymous delegate. Have a look at line 5 of the previous snippet.

  • Let’s cut out the word delegate (no need for that every time)
  • Since we know that the first parameter is string, we don’t need to explicitly say string. So let’s cut out the word string.
  • Lastly, do you see the curly braces before and after the body (lines 6 & 8). If it’s a one liner, it’s really not needed. Let’s replace the first brace with => to signify the body is going to follow and remove the last brace.

What are we left with? Well, let’s see:

public class DelegateTest
{
  public void Go()
  {
    SomeMethod((text) => Console.WriteLine("Logging: " + text));
  }

  public void SomeMethod(Action<string> logger)
  {
    logger("This is my text to be logged");
  }
}

We’ve gone from 18 lines to 12 lines and the code is much cleaner. Notice that our anonymous delegate has now magically transformed to a lamdba expression?

Well, there we have it. Proof that lamda is nothing more than an abbreviated anonymous delegate and the story of how it came to be.

I hope that made sense and that I didn’t miss an important step in the “evolution of the delegate”.

Advertisements

2 thoughts on “The evolution of the delegate

  1. Good read. Is it safe to say that the way we first declared delegates (.Net 1) is now obsolete. Since Action and Func and you Predicate (you missed this one) has been around, you’ll never need the old way of declaring delegates.

    Like

    • Thanks for the comment Romar. Yes you’re absolutely right, I did leave out Predicate, because technically a Predicate is simply a Func delegate with a boolean return type.

      As for .Net 1 delegate declaration being obsolete, I have to unfortunately disagree with you for 2 reasons.
      1) Action and Func have overloads enough for 16 parameters, so if your delegate takes 20 parameters (let’s hope that’ll never happen), you cannot use Action or Func.
      2) This is a bigger reason for me, when you create any reusable piece of code that accepts delegates as a parameter for instance, using Action or Func will loose you the ability to name the delegate and name all the parameters. E.g. You want to call a method that requires an Action<string, string, string> delegate as a parameter. So what is the first string parameter? the second? the third? You would have to look go into the method or hope for good documentation.
      Alternatively using delegate void ReplaceDelegate(string word, string oldString, string newString); you would immediately know that this is a delegate which will take a word and replace the old string with the new string, just from the descriptive delegate name and parameter names. You’ve now just saved the developers using your code the frustration of figuring it out.

      I hope that makes sense and answers your question.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s