Professor Mustard Programming
 

Programming 101 - Unit 06 - Other Features

06f) Delegates
 
Delegates are a cool little feature that (more or less) allow us to store a series of method calls as a variable. It's easier to show than explain, though, so let's just barge right into an example. First, we declare a delegate...

delegate void MoveDelegate(string direction);


Like structs, classes and enums, delegates can be declared outside of a containing class (although you're certainly welcome to declare them within a class if it makes sense). All of the methods we throw into a delegate must have the same signature, so this declaration is where we determine the return type and parameters of the methods. In this case, we've said that "MoveDelegate" will deal in methods that return void, and take one string as a parameter. "MoveDelegate" is the name we've provided for the delegate; you should make it something that implies what purpose its methods will serve. And that's it for declaration; now we're ready to start using it!

The whole idea of a delegate is storing method calls, so the next thing we should probably do is write some methods! They'll have to match the delegate's signature, but the actual code can be anything we want...

static void Run(string direction)
{
  Console.WriteLine("Ran " + direction);
}

static void Jump(string direction)
{
  Console.WriteLine("Jumped " + direction);
}

static void Crawl(string direction)
{
  Console.WriteLine("Crawled " + direction);
}



Finally, we declare an instance of our delegate, and load it up with method calls:

[STAThread]
static void Main(string[] args)
{
  MoveDelegate mover = new MoveDelegate(Run);
  mover += new MoveDelegate(Jump);
  mover += new MoveDelegate(Crawl);
  mover += new MoveDelegate(Run);
  mover += new MoveDelegate(Run);
}



We start out by declaring a delegate with one method call to Run(). As you can see, though, we are able to load up the delegate with as many method calls as we want. We can even add the same method call multiple times. The last step is actually calling the delegate:

mover("forward");


Here's the key point. After doing all this work, what happens when we run the line of code pictured just above? Well, we call our MoverDelegate, "mover", and pass in the string "forward" as the parameter. "mover", in turn, calls Run(), Jump(), Crawl(), Run() and then Run(), in the same order we added them to the delegate, passing the same parameter to each one. We've just called five methods with one line of code!

Think of the potential that this mechanism has. There are no appreciable limits to the number of method calls you can add to a delegate instance. You can build up a huge routine of method calls over the course of a program, and call them all at once whenever you want! You see, even when you use "if" statements to decide which method to call next, the actual logic and program flow is still hard-wired into your code. Delegates allow us to take another step away from this approach, by making the calls themselves dynamic. They can be more effective, too, when you have to make the same flow decision many times. An "if" statement has to be re-evaluated every time you run it, whereas a delegate only has to be "loaded" up with methods once, after which it can be called again and again with no further decision making.

By the way, what happens if your delegate has a return type, and you use it to make multiple method calls that all return a value? In this case, the value returned by the last method call will be the value returned by the delegate. This isn't overly useful, but delegates are most commonly used with void methods, anyway.

As with many features in this unit, the idea of delegates might sound pretty cool, but it may not be immediately obvious how you could use them in your own programs. Be patient. No one says you have to use all of CSharp's features in every program, so just keep them in mind, for now. The code sample below summarizes their basic use.



using System;

namespace F_Delegates
{
  // Delegate with VOID return type, and one STRING parameter
  // We can declare delegates inside classes, too
  delegate void MoveDelegate(string direction);

  class Class1
  {
    static readonly string ln = Environment.NewLine;

    [STAThread]
    static void Main(string[] args)
    {
      // Declare an instance of the delegate, and add some method calls to it.
      MoveDelegate mover = new MoveDelegate(Run);
      mover += new MoveDelegate(Jump);
      mover += new MoveDelegate(Crawl);
      mover += new MoveDelegate(Run);
      mover += new MoveDelegate(Run);

      // Now we can call all five methods at once, with
      // just one call to our delegate!
      mover("forward");
      
      PromptForExit();
    }

    static void Run(string direction)
    {
      Console.WriteLine("Ran " + direction);
    }

    static void Jump(string direction)
    {
      Console.WriteLine("Jumped " + direction);
    }

    static void Crawl(string direction)
    {
      Console.WriteLine("Crawled " + direction);
    }

    static void PromptForExit()
    {
      Console.Write(ln + "Program complete! Hit enter to exit...");
      Console.ReadLine();
    }
  }
}