CupCode Gamers

Under Construction

We are switching from using a CMS to a custom built website. Please bear with us as we migrate our content manually.

Beginner’s Guide to Delegates

Monday, March 27, 2017

In C and C++ there were function pointers. Delegates are C#’s object oriented and type safe answer to function pointers. Let’s say you have the following two methods:

public void WriteOut()
{
Console.WriteLine("WriteOut method called");
}
public void WriteIn()
{
Console.WriteLine("WriteIn method called");
}

In your code, you want to call one of these based on the value of the integer selected, you could write it using an if statement like this:

if(selected == 1)
{
WriteOut();
}
elseif(selected == 2)
{
WriteIn();
}
else
{
Console.WriteLine("Hunh?");
}

But what happens if you add more methods later? You’d have to add more else statements, eventually resulting in unwieldy code and really long source files, especially if you have hundreds of such methods.So what’s a better option? Use an array of delegates. Before I show you how to do this, let me show you how to declare and use a delegate to begin with.First we declare the delegate:

delegate void SimpleDelegate();

Now, let’s create an instance of the delegate and assigne the WriteOut method:

SimpleDelegate d = new SimpleDelegate(WriteOut);

Now, instead of calling WriteOut directly, we can call the delegate like this:

d();

Yes, that’s right, we just called the WriteOut method by calling on the delegate d. But how is this handy? Well, think of it this way, now that we have a reference to a method we can create an array containing the references. This is something we couldn’t do with method names. For instance:

delegate void SimpleDelegate();
SimpleDelegate[] void d() = new SimpleDelegate[2]();
d[0]() = SimpleDelegate(WriteIn);
d[1]() = SimpleDelegate(WriteOut);

Now that we’ve setup our delegate array, the next and final piece of the puzzle is to iterate through the array calling each referenced method.

For(int a = 0; a < 2; a++)
{
d[a]();
}

That’s it, we’ve just called both functions from within a for loop. Realistically we’d never loop through the entire array calling each referenced method, instead we’d want to call just one method based on the value of an integer, but this example provides you all the necessary knowledge to get it done.What if we did want to call every method all at once? Well, there’s another option with delegates that we haven’t yet explored. Let’s rewrite our delegate definitions:

delegate void SimpleDelegate();
SimpleDelegate void d() = new SimpleDelegate();
d() = SimpleDelegate(WriteIn);
d() += SimpleDelegate(WriteOut);
d();

In the above code we’ve declared a single delegate. In the second line we assign the WriteIn method to the delegate, and in the third line we add the WriteOut method to the delegate. When we call the delegate in line four it will call the WriteIn method, then it will call the WriteOut method. We’ve now called all the delegate’s methods without having to use a for loop.But what if we later want to remove a method from a delegate? Continuing the previous example we can do it like this:

d() -= SimpleDelegate(WriteOut);

Now the WriteOut method is removed from the delegate, but the delegate still contains a reference to the WriteIn method.There are several rules regarding delegates that you should know of. First, when using a delegate all methods being assigned must have the same parameter list as when you declare the delegate. You can declare a delegate with parameters like:

Delegate int SimpleDelegate(intNumber1, intNumber2);

All methods being assigned to a delegate must also provide the same type return value as the delegate declared. In the preceeding example, all assigned methods must accept two integer parameters and return one integer value.If you call upon a delegate that contains references to two or more methods, the values passed into the parameters are counted as local variables for as long as the delegate runs. If the first method modifies the value of a passed parameter, the modified value is what is passed to the second method, and so on and so forth until all referenced methods have ran.

Delegates by Example Part 1

Monday, March 27, 2017

using System;
using System.Collections.Generic;

/* This example shows how you can use delegates in a generic list to call several methods in sequence
* The final output of this tutorial will be:
*
* WriteOut method called
* 2
* WriteIn method called
* 0
*
* As you see, the methods are called in the order they were added to the generic list.
* Also note that the value of the argument is the same for each call, that's because using this
* method, you are supplying the value seperately to each function in the delegate.
*/
namespace DelegatesTutorial
{
class Example1
{
// Declare the delegate to be used later, this sets the return type and expected parameters
// for the delegate. All functions to be included must have the same return type and parameter list.
delegate void myDelegate(int number);
public Example1()
{
// We'll create a generic array of delegates
List TheDelegates = new List();
// Now we'll add the functions to the delegate array list
TheDelegates.Add(new myDelegate(WriteOut));
TheDelegates.Add(new myDelegate(WriteIn));
// Here we'll loop through every delegate in the list, calling it's associated function
foreach (myDelegate simple in TheDelegates)
{
simple(1);
}
}
// These are the functions to be called by the delegate
void WriteOut(int number)
{
Console.WriteLine("WriteOut method called");
int i = ++number;
Console.WriteLine(i.ToString());
}
void WriteIn(int number)
{
Console.WriteLine("WriteIn method called");
int i = --number;
Console.WriteLine(i.ToString());
}
}
}

Delegates by Example Part 2

Monday, March 27, 2017

using System;
/* This example illustrates building one delegate to call several functions in sequence
* The final output of this example will be
*
* WriteOut method called
* 2
* WriteIn method called
* 1
* WriteIn method called
* 0
*
* Notice that even though we pass 1 to the arguments of the delegate, the first iteration of
* WriteIn recieves the value of 2. This is because WriteOut modified that value to 2.
* Using this method, all functions share the same argument, thus if the first function modifies
* it's value, it's modified for all the rest of the functions in the list.
* This is very important in deciding whether to use the method in example1 or example2, as it
* can significantly change the resulting output of the program.
* (NOTE: This is not true on every compiler, test to find out)
*/
namespace DelegatesTutorial
{
class Example2
{
// Declare the delegate to be used later, this sets the return type and expected parameters
// for the delegate. All functions to be included must have the same return type and parameter list.
delegate void MyDelegate(int number);
public Example2()
{
// We'll create a single delegate from MyDelegate and assign WriteOut to it
MyDelegate TheDelegate = new MyDelegate(WriteOut);
// Now add WriteIn to the existing delegate
TheDelegate += new MyDelegate(WriteIn);
// Call the functions of the delegate
TheDelegate(1);
// Remove the WriteOut delegate
TheDelegate -= new MyDelegate(WriteOut);
// Call the functions of the delegate again.
TheDelegate(1);
}
// These are the functions to be called by the delegate
void WriteOut(int number)
{
Console.WriteLine("WriteOut method called");
int i = ++number;
Console.WriteLine(i.ToString());
}
void WriteIn(int number)
{
Console.WriteLine("WriteIn method called");
int i = --number;
Console.WriteLine(i.ToString());
}
}
}

Delegates by Example Part 3

Monday, March 27, 2017

using System;
using System.Collections.Generic;

/* This example shows one way of using delegates in a command parser
 * The final output of this example will be
 * doHello function called.
 * doWorld function called.
 * doThere function called.
 * doWorld function called.
 *
 * Notice in this example the delegate and the associated functions don't accept any parameters.
 * Delegates don't have to have parameters, infact, they can also have a return value
 */
namespace DelegatesTutorial
{
  // Declare the delegate to be used later, this sets the return type and expected parameters
  // for the delegate. All functions to be included must have the same return type and parameter list.
  delegate void myDelegate();
  // Declare a class to contain the keyword and the delegate
  class Command
  {
    public string Keyword = default(string);
    public myDelegate CmdPointer;
    public Command(string sKeyword, myDelegate dPointer)
    {
      Keyword = sKeyword;
      CmdPointer = dPointer;
    }
  }

  class Example3
  {
    // Instead of creating a list of delegates, we'll create a list of the command class
    List<Command> Commands = new List<Command>();

    public Example3()
    {
      // We'll now add three commands
      Commands.Add(new Command("hello", new myDelegate(doHello)));
      Commands.Add(new Command("there", new myDelegate(doThere)));
      Commands.Add(new Command("world", new myDelegate(doWorld)));

      // Let's use the command parser
      CommandParser("Hello");
      CommandParser("world");
      CommandParser("there");
      CommandParser("world");
    }

    // Here's the actual command parser. It simply compares text then calls the
    // appropriate function through the delegate
    void CommandParser(string CommandToParse)
    {
      foreach (Command c in Commands)
      {
        if (c.Keyword.ToLower() == CommandToParse.ToLower())
        c.CmdPointer();
      }
    }

    // These are the functions to be called by the delegate
    void doHello()
    {
      Console.WriteLine("doHello function called.");
    }
    void doThere()
    {
      Console.WriteLine("doThere function called.");
    }
    void doWorld()
    {
      Console.WriteLine("doWorld function called.");
    }
  }
}

ErrorLog Events

Monday, March 27, 2017

I’ve written a very helpfull ErrorLog Event class. When used correctly, it raises an event that you can use to process errors however you please.

The main benefit to using this class is if you include it in a Code Library project, it provides a means of returning error messages to the application so the application can handle it.

It does not however return the Exception itself, but you can modify it easily to do that if you need to. What it does return is a string containing all error messages and stacktrace’s for the error, including any and all inner exceptions.

This tutorial is also a very good, and basic example of using Delegates to setup event handling between a DLL and an application. (Remember, DLLs normally only communicate with applications via return values from functions. This allows extra communications outside of return values.)

This tutorial is no longer offered as a viewable Compilr project, but the source code is available below. The code has been modified to use function overloads so that strings and exceptions are passed using the same method name.

NOTE: This code will not do anything by itself. It must be included in a project.

NOTE: While this code does work to perform some simple error logging. I would highly advise using something like log4net instead.

DEBUGGING NOTE: Normally we want error handlers in every function, however since this is the error handler, if we have errors here we really have some serious issues, but will never know about them as they won’t be reported back to the application anyways. For this reason, we’ll trap the errors here, but not handle them. This way at least the application won’t crash.

USAGE: To use this, simply include it in your project, then write your catch blocks like this:

catch(Exception ex)
{
ErrorLog.Add(ex);
}

Then in your application, in it’s main method, before anything else, put this:

ErrorLog.Added += newErrorLogAdded(ErrorLog_Added);

Then simply add the following function, and modify it to handle the error messages as you please.

void ErrorLogAdded(stringMessage)
{
Console.WriteLine(Message);
}

Here’s the class:

using System;

namespace Utilities
{
// We need a delegate for the event, to be used in the application for
// detecting when the event was fired, and declaring what method to handle
// it with
public delegate void ErrorLogAdded(stringMessage);
public static class ErrorLog
{
// Create the event
public static event ErrorLogAdded Added;

// Method to add a string to the error handler, and fire the event
public static void Add(stringResponse)
{
try
{
// There's no processing to be done here, so just fire the event off
Added(Response);
}
catch { }
}
// Method to add an exception to the error handler, and fire the event
public static void Add(Exception ex)
{
try
{
// This is usefull for avoiding error messages in a threaded environment
// where the thread is shutting down.
if(ex.Message != "Thread was being aborted.")
{
// Start the message with the top error message and stacktrace
string Message = string.Format("{0}\r\n{1}", ex.Message, ex.StackTrace);
// Create a local copy of the exception
Exception ce = ex;
while(ce.InnerException!=null)
{
// Add the inner exception message and stacktrace to the string
Message = string.Format("{0}\r\n{1}\r\n{2}", Message, ce.InnerException.Message, ce.StackTrace);
// Move to the inner exception for the next iteration
ce = ce.InnerException;
}
// We've built the message string, fire the event off.
Added(Message);
}
}
catch { }
}
}
}