To create a delegate we first declare our delegate type. We can think of this as being similar to declaring a new class:
delegate Int32 SomeMethodDelegate(Int32 input);
And then we can use an instance of this delegate to "wrap" our method call:
Using the same method we had earlier...
static Int32 SomeMethod(Int32 input)
{
return input + 1;
}
Somewhere else in our code we can instantiate the delegate as follows. (note: in the sample below, "Program" is a static class)
// f is a delegate object that "wraps" the call
SomeMethodDelegate f = new SomeMethodDelegate(Program.SomeMethod);
Now we can use our delegate instance to call the same method.
Int32 y = f(5);
The compiler is smart enough to figure out which kind of delegate to instantiate so we can use a shorter syntax if we want. We are actually still very explicitly instantiating a delegate by telling the compiler what kind of delegate we want with the declaration on the left hand side of the assignment operator. (note: I don't need to specify the object in the following code sample because the delegate instantiation is in the same class as "SomeMethod()".)
// f is a delegate object that "wraps" the call
SomeMethodDelegate f = SomeMethod;
Adding Generics in the Mix....
We may not need a bunch of explicit delegate types lying around in our project (although sometimes it is good to have a few for more readable code) and can use generics to consolidate all delegates with similar surface areas (the same input and output types):
delegate TOutput GenericDelegate<TInput, TOutput>(TInput input);
Now we can wrap our method using our generic delegate just as before:
// using generic delegate definition
GenericDelegate<Int32, Int32> f = SomeMethod;
Int32 y = f(5);
Note: this GenericDelegate<> is the same as the Func<> delegate defined in the 3.0 framework
// using built-in Func<> generic delegate definition (3.0 framework)
Func<Int32, Int32> f = SomeMethod;
Int32 y = f(5);
We also have the generic Action<> delegate which wraps a method with no return value and a Predicate<> delegate which is usually used to perform tests returns a Boolean and a Comparison<> delegate to compare two objects.
Anonymous Delegate....
We may want to have a method that does not belong to a class (which is why we call it anonymous) and can declare it in-line using the following syntax. Notice how the method has no "outer shell" definition. The "SomeMethodDelegate" object is still instantiated and points to the in-line method.
// in-line method definition... it has no name so it is anonymous
SomeMethodDelegate f = delegate(Int32 x) { return x + 1; };
Int32 y = f(5);
We could use the same syntax to instantiate our anonymous delegate as well
// in-line method definition... it has no name so it is anonymous
Func<Int32, Int32> f = delegate(Int32 x) { return x + 1; };
Int32 y = f(5);
If you haven't done it already, it is worth taking some time and using the debugger to step through code containing anonymous method to get a better understanding of how the execution of the code flows.
Closures...
One of the ultra-cool features of anonymous methods is that they support closures which is a concept coming out of functional programming languages. Closures are VERY powerful and can help us keep code more concise. When a language supports closures it basically means that the a method body has access to any local variable declared outside it's scope.
String hi = "Hello World";
Action<String> myAction = Console.WriteLine;
Func<String> myStringGetter = delegate()
{
// the variable "hi" is available here because
// anonymous methods support closure
return hi;
};
myAction(myStringGetter());
Lambda Expressions...
The syntax for declaring an anonymous delegate is pretty verbose (and kind of ugly). With the 3.5 Framework, we have a much shorter (and nicer) way to instantiate anonymous delegates using lambda expressions.
SomeMethodDelegate f = x => { return x + 1; };
Int32 y = f(5);
The input parameter is the variable before the => symbol.
x => { return x + 1; };
The body of the method is what appear after the => symbol.
x => { return x + 1; };
Because the "SomeMethodDelegate" signature says we have to input a Int32 and output an Int32, the compiler knows that our "x" variable should be an integer (see if makes more sense if you try and match the delegate signature (below) up with our lambda expression (above).
delegate Int32 SomeMethodDelegate(Int32 input);
We can use an even shorter syntax for declaring simple anonymous methods with the lamba expression and leave off the brackets and the "return" keyword. The following code uses the simpler syntax:
// can use shorter syntax if you don't want brackets or "return" keyword
SomeMethodDelegate f = x => x + 1;
Int32 y = f(5);
Console.WriteLine(y);
Of course we can use lambda expressions with generic delegates as well. The following shows the lambda expression syntax for instantiating a generic delegate with two input parameters. If our delegate has multiple input parameters, we must group them in parenthesis:
Func<Int32, Int32, Int32> add = (m, n) => m + n;
Int32 y = add(3, 4);
What Are They Good For?
Delegates are used most frequently for subscribing to events so we'll look at how we can utilize delegates with an object that exposes an event.
Here is our dummy event raising object which could just as easily be a UI form or control.
class Broadcast
{
private event EventHandler m_somethingHappened;
public event EventHandler SomethingHappened
{
add { m_somethingHappened += value; }
remove { m_somethingHappened -= value; }
}
public void Fire()
{
if (null != m_somethingHappened)
m_somethingHappened(this, EventArgs.Empty);
}
}
Example 1:
Let's say we instantiate a new Broadcast object and want to wire up the events using anonymous delegates:
Broadcast bc = new Broadcast();
bc.SomethingHappened += delegate(Object sender, EventArgs args)
{
Console.WriteLine("Something Happened (anonymous method)");
};
bc.Fire();
Example 2:
We can also wire up our Broadcast object using a lambda expression:
Broadcast bc = new Broadcast();
bc.SomethingHappened += (snd, arg) => Console.WriteLine("Something Happened (Lambda)");
bc.Fire();
Example 3:
There are also many other places where delegates can be useful. Another scenario is when we need to pass a delegate to another method for execution. See if you can figure out what's happening in the following code.
If we have a method declared that accepts a delegate and another item defined as its input parameters, it will execute the method using the second parameter as input.
static TOutput ExecuteSomething<TInput, TOutput>(Func<TInput, TOutput> mthd, TInput input)
{
return mthd(input);
}
Can you figure out what will happen with the following call to our method?
Console.WriteLine(ExecuteSomething(g => g - 5, 20));
Example 4:
Many of the methods off of the List<> object take delegates as arguments.
namespace System.Collections.Generic
{
public class List<T> {
public List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter);
public bool Exists(Predicate<T> match);
public T Find(Predicate<T> match);
public List<T> FindAll(Predicate<T> match);
public int FindIndex(Predicate<T> match);
public int FindIndex(int startIndex, Predicate<T> match);
public int FindIndex(int startIndex, int count, Predicate<T> match);
public T FindLast(Predicate<T> match);
public int FindLastIndex(Predicate<T> match);
public int FindLastIndex(int startIndex, Predicate<T> match);
public int FindLastIndex(int startIndex, int count, Predicate<T>
public void ForEach(Action<T> action);
public int RemoveAll(Predicate<T> match);
public void Sort(Comparison<T> comparison);
public bool TrueForAll(Predicate<T> match);
}
}
As an example, let's use the FindAll() method to filter a list of integers using an anonymous delegate written in lambda syntax:
Random r = new Random();
List<Int32> myList = new List<int>();
// fill the list with random integers
for (int i = 0; i < 1000; i++)
myList.Add(r.Next());
// filter the list using a delegate
List<Int32> myFilteredList = myList.FindAll(x => x < Int32.MaxValue/4);
Well, that's about it for the ways we have to call methods and some of the uses of delegates. I hope you enjoyed the tour.
Until Next Time,
Happy coding