Skip to main content

C# Tutorial - The Built-In Generic Delegate Declarations [Beginner]


Lambda and anonymous functions and delegates are all extremely useful and awesome features of C#, but sometimes using them in any way but the simplest fashion can feel like hard work. This is often because C# is a strictly typed language, and so the compiler always needs to be able to resolve the types for your delegates - to determine where they can and can't be used.

So sometimes, when you find that you need to pass around a delegate, you end up needing to write a delegate declaration. And really, even though it is generally a single simple line of code, it can be very annoying. First, because you have to remember the syntax for writing a delegate declaration, and second because you have to find someplace useful to put it (which is doubly annoying - because you were probably using the anonymous function in the first place because you didn't want have to pull it out and put it somewhere specific).

For instance, take a look a tutorial from about a year ago, the tutorial on how to use the lambda operator. In that tutorial, we wrote a method that would take a delegate and fold it across the rest of the arguments to the function. It looked like this:
public delegate int FoldIntDelegate(int a, int b);

public int Fold(FoldIntDelegate fid, params int[] list)
{
  int result = 1;
  foreach (int i in list)
    result = fid(result, i);
  return result;
}
 
That first line is the line that I'm complaining about - the line I'd like to get rid of. Well, guess what? You can! There are a whole bunch of basic delegate declaration in the System namespace that cover almost all of the common delegate declaration cases by using generics. For instance, that Fold method could have been written like this:
public int Fold(Func<int, int, int> fid, params int[] list)
{
  int result = 1;
  foreach (int i in list)
    result = fid(result, i);
  return result;
}
 
So instead of using the custom FoldIntDelegate from the first block of code, here we are using a generic Func delegate (one that allows you to specify two argument types and a return value type. The declaration for this delegate looks like this:
public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);
 
This delegate declaration will cover any two argument method that has a return value - all you have to do is provide the types for the arguments/return values.

But that's not all - there is a lot more where that came from.

The Funcs
The Funcs are the delegate declaration for delegates that return a value, and were first introduced in .NET 3.5. We have already seen one, the two argument Func. But there are Func declarations for everything from no arguments to 4 arguments:

And here are what their declarations look like:
public delegate TResult Func<TResult>();
public delegate TResult Func<T1, TResult>(T1 arg1);
public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);
public delegate TResult Func<T1, T2, T3, TResult>(T1 arg1, T2 arg2, T3 arg3);
public delegate TResult Func<T1, T2, T3, T4, TResult>(T1 arg1, 
    T2 arg2, T3 arg3, T4 arg4);
 
So if you need more than 4 arguments, you have to make your own declaration. But most of the time, you don't, and so this covers many of the common signatures.

I can hear you now, though - what if I don't want to return a value? Well, don't worry, that is here too:

The Actions
The Actions are the delegate declarations to use when your delegate doesn't return a value. Again, there are Actions for everything from zero (commonly called a thunk) to 4 arguments. And just as a note, the zero argument Action is not actually a generic delegate - cause there is no need for it to be (there are no types to deal with). The single argument Action was introduced in .NET 2, and the rest of them were added in .NET 3.5.

And, for your viewing pleasure, their declarations:
public delegate void Action();
public delegate void Action<T1>(T1 arg1);
public delegate void Action<T1, T2>(T1 arg1, T2 arg2);
public delegate void Action<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3);
public delegate void Action<T1, T2, T3, T4>(T1 arg1, 
    T2 arg2, T3 arg3, T4 arg4);
 
So between those 10 declarations - the Funcs and the Actions - that about covers pretty much every type of delegate you would ever want to pass around. But there are a couple others that you should probably know about.

Specialized Declarations
All four of these specialized delegates have been around since .NET 2, and actually each of them could be replaced by a Func or an Action. But they do serve useful and specific purposes, and generally I think it is a good idea to use one of these (as opposed to the more generic Func/Action) when you are actually doing what these declarations are meant for.
 
public delegate int Comparison<T>(T x, T y);
public delegate TOutput Converter<TInput, TOutput>(TInput input);
public delegate bool Predicate<T>(T obj);
public delegate void EventHandler<TEventArgs>(Object sender,  
    TEventArgs e) where TEventArgs : EventArgs;
 
First off here we have the Comparison declaration (which could also be seen as a two argument Func). Generally a good one to use if you are writing a delegate that compares two items of the same type. A couple places in the framework expect delegates of type Comparison, most notably the Sort function on List.

Next, we have Converter (which is very similar to a two argument Func). This is used when, as the name might imply, the delegate is converting from the input type to the output type. The place where I use this all the time is also on List, the ConvertAll method.

Following Converter, we have Predicate. This can be looked at as an even more specialized Converter - one that always converts to a boolean. Delegates of type Predicate are expected all over the place, but again, I generally use them when dealing with Lists, like the TrueForAll method. Surprisingly, though, many of the LINQ extension methods don't expect predicates, but instead expect Func<T, bool>.

And then we get to our last special case, the EventHandler declaration. Now this here is a handy beast, for whenever you want an event that doesn't use the standard EventArgs. This is because instead of writing code like this:
public class CustomEventArgs : EventArgs
{
  /*My CustomEventArgs Class Content */
}
public delegate void CustomEventHandler(object sender, CustomEventArgs e);
public event CustomEventHandler SomeEvent;

You can write it like this:
public class CustomEventArgs : EventArgs
{
  /*My CustomEventArgs Class Content */
}
public event EventHandler<CustomEventArgs> SomeEvent;
 
You might say "bah! It is just a two argument action!." You would be right, except that the EventHandler delegate declaration helps enforce a couple useful things - first, that the event has a sender, and second, that whatever event args class is being passed is derived from EventArgs (that is the where clause at the end of the declaration).

That is it for the built-in delegate declarations! Those signatures should really cover almost every delegate that you would ever want to pass around, and hopefully lowers even further the barrier between you and common use of anonymous functions.

Comments

Popular posts from this blog

C# Snippet - Shuffling a Dictionary [Beginner]

Randomizing something can be a daunting task, especially with all the algorithms out there. However, sometimes you just need to shuffle things up, in a simple, yet effective manner. Today we are going to take a quick look at an easy and simple way to randomize a dictionary, which is most likely something that you may be using in a complex application. The tricky thing about ordering dictionaries is that...well they are not ordered to begin with. Typically they are a chaotic collection of key/value pairs. There is no first element or last element, just elements. This is why it is a little tricky to randomize them. Before we get started, we need to build a quick dictionary. For this tutorial, we will be doing an extremely simple string/int dictionary, but rest assured the steps we take can be used for any kind of dictionary you can come up with, no matter what object types you use. Dictionary < String , int > origin = new Dictionary < string , int >();

C# Snippet - The Many Uses Of The Using Keyword [Beginner]

What is the first thing that pops into your mind when you think of the using keyword for C#? Probably those lines that always appear at the top of C# code files - the lines that import types from other namespaces into your code. But while that is the most common use of the using keyword, it is not the only one. Today we are going to take a look at the different uses of the using keyword and what they are useful for. The Using Directive There are two main categories of use for the using keyword - as a "Using Directive" and as a "Using Statement". The lines at the top of a C# file are directives, but that is not the only place they can go. They can also go inside of a namespace block, but they have to be before any other elements declared in the namespace (i.e., you can't add a using statement after a class declaration). Namespace Importing This is by far the most common use of the keyword - it is rare that you see a C# file that does not h

C# WPF Printing Part 2 - Pagination [Intermediate]

About two weeks ago, we had a tutorial here at SOTC on the basics of printing in WPF . It covered the standard stuff, like popping the print dialog, and what you needed to do to print visuals (both created in XAML and on the fly). But really, that's barely scratching the surface - any decent printing system in pretty much any application needs to be able to do a lot more than that. So today, we are going to take one more baby step forward into the world of printing - we are going to take a look at pagination. The main class that we will need to do pagination is the DocumentPaginator . I mentioned this class very briefly in the previous tutorial, but only in the context of the printing methods on PrintDialog , PrintVisual (which we focused on last time) and PrintDocument (which we will be focusing on today). This PrintDocument function takes a DocumentPaginator to print - and this is why we need to create one. Unfortunately, making a DocumentPaginator is not as easy as