Delegates Tutorial - MC++ and C# - The dual perspective

Introduces and walks you through the use of delegates in .NET. Compares and contrasts the different approaches that MC++ and C# adopt in the use of delegates. With examples in both languages

Introduction

One of my favorite features about good old C was function pointers. Those of
you who haven't used function pointers missed out on the fun. When C++ was out
we also had pointers to member functions. The basic problem with function
pointers and pointers to member functions is that, neither of them is type-safe.
The .NET framework has a class named Delegate in the System namespace. Delegates
are the .NET surrogate for function pointers and pointers to member functions.
The advantage with delegates is that delegates are fully managed objects that
are also type safe. A delegate basically encapsulates a method with a particular
set of arguments and return type. You can encapsulate only a method that matches
the delegate definition in a delegate. Delegates can encapsulate both static
methods of a class as well as instance methods.

Delegates are called single-cast delegates when they encapsulate a single
method, and are called multi-cast delegates when they encapsulate more than one
method. Multi-cast delegates are useful as event-handlers. Multi-cast delegates
should not be confused with an array of delegates. Multi-cast delegates are
derived from the MulticastDelegate class which is a child class of the Delegate
class. When a multi-cast delegate is invoked, the encapsulated methods are
called synchronously in the same order in which they were added to the
multi-cast delegate. Managed C++ and C# offer language specific features that
allow us to work with delegates directly without having to call member methods
of the Delegate class or the MulticastDelegate class. Through some very simple
examples, I'll show how delegates are used, both in Managed C++ and in C#.

Basic operations

Declaring delegates

In Managed C++ we use the __delegate keyword to declare delegates. In C# we
use the delegate keyword. In both cases the compiler will automatically inherit
from System::Delegate. There is no difference in the manner of declaration
between single-cast and multi-cast delegates. I presume that internally a
single-cast delegate is treated as a multi-cast delegate with just one
encapsulated method.

Binding delegates to methods

For a single-cast delegate we simple use the default delegate constructor
which the delegates inherit from System::Delegate. The constructor takes two
arguments, where the first argument is the object whose method we are binding to
the delegate and the second argument is the address of the method. For static
methods the first argument can be 0. In C# things are simplified further in that
we don't need to pass the first argument. The C# compiler figures it out for us.

For multi-cast delegates we use the Delegate.Combine method which has two
overloads. One overload takes an array of Delegate objects and combines them. The
other overload takes two Delegate objects and combines them. Both return a
Delegate object which we need to cast to our delegate type. Again, C#
programmers have it really easy. The + and += operators has been overloaded in
C# and adding a delegate to another delegate is done simply by using the +
operator on any number of delegates.

For removing a delegate from the invocation list of a multi-cast delegate we
use the Delegate.Remove method. This method takes two arguments. The first
argument is the source delegate which may contain one or more encapsulated
methods. The second argument is the delegate object that we wish to remove from
the multi-cast delegate. The method returns a Delegate object which we cast to
the delegate type we are expecting. I guess you might have guessed by now that
C# would have a simpler way of doing things. In C# the - and -= operators have
been overloaded so that you can actually subtract a delegate from a multicast
delegate.

Invoking a delegate

When we invoke a delegate, the encapsulated methods are synchronously called
in the order in which they were attached to the delegate. In Managed C++ this is
achieved by calling a method called Invoke. This method is added to our delegate
class by the compiler and will have the same signature as our delegate. In C#,
we need not bother even this much, and all we have to do is to call a method
that has the same name as our delegate object, and pass it any required
arguments. The Invoke mechanism described here is based on early binding. We
know exactly what the delegate signature is and thus we can invoke our delegate.
It might interest you to know that the Invoke method is actually added by the
respective compilers and is not inherited from the Delegate class. For late
bound invocation you can use the DynamicInvoke method. But this article will not
cover late bound invocation as it's outside the scope and latitude of this
article.

Now we'll see some small sample programs that will make things clearer to
you. Compile and run the programs and try and figure out whether the output you
get makes sense. If you are confused, don't worry too much, just read the
article once more and then think about it for some time. Things will slowly make
sense. There are also some good articles on MSDN dealing with delegates which
will enlighten you further.

Program 1

In this program we'll see how to declare and use a single-cast delegate. Our
delegate takes a String as argument and returns a String as well. We'll first
assign an instance method of an object to the delegate and then invoke the
delegate. Then we'll assign a static method of a class to the same delegate
object and again invoke the delegate.

Program 2

Now we'll see an example of using a multi-cast delegate. Our delegate takes
zero arguments and returns void. We'll first create two single-cast delegates,
one based on an instance method and the other one based on a static method. Then
we'll create our multi-cast delegate by combining the two delegates. Now we
invoke our multi-cast delegate. From the output you should be able to figure out
the order in which the encapsulated methods were called. Now we remove one of
the delegates from our multi-cast delegate and again invoke it. The output
should match your understanding of the working of delegates.

Program 3

In this program we will see how we can pass a delegate object as an argument to a
method. The groovy thing about this is that the called method has absolutely no
idea what the passed delegate is referencing. In our little example we have a
delegate that takes an int and returns an int. We'll write two methods
that can be assigned to the delegate, one that returns the square of the passed
number and the other that returns the cube of the passed number.

Conclusion

Well, summing up, a delegate is just about the equivalent of function
pointers except that delegates are objects and are type safe. Unlike function
pointers delegates can reference both static and instance methods of a class.
Delegates inherit from MulticastDelegate. The compiler adds an Invoke method to
your delegate object, which has the same signature and return type as the
delegate. Delegates can be single-cast or multi-cast. Multi-cast delegates are
formed by combining several delegates. Delegates can be passed as arguments to
functions.

The great thing
about delegates is that they don't care about the class whose member function
they are referencing. All it cares about is that the arguments passed and the
return type match that of its own. We can thus use delegates for
black-box-invocation, where we don't know what member function the delegate is
pointing to. Delegates are very useful as event handlers. When an event is
raised the event handlers of the subscribing classes are invoked through
delegates.

Share

About the Author

Nish Nishant is a Software Architect/Consultant based out of Columbus, Ohio. He has over 15 years of software industry experience in various roles including Lead Software Architect, Principal Software Engineer, and Product Manager. Nish is a recipient of the annual Microsoft Visual C++ MVP Award since 2002 (13 consecutive awards as of 2014).

Nish is an industry acknowledged expert in the Microsoft technology stack. He authored C++/CLI in Action for Manning Publications in 2005, and had previously co-authored Extending MFC Applications with the .NET Framework for Addison Wesley in 2003. In addition, he has over 140 published technology articles on CodeProject.com and another 250+ blog articles on his WordPress blog. Nish is vastly experienced in team management, mentoring teams, and directing all stages of software development.

I think what is referred to here is, that the signatures (i.e. the return type and the expected parameter types) of a delegate and the actual linked method(s) must match or you get a compile error. So you could call a Delegate "more" type-safe than a function pointer, because the function pointer doesn't ensure that the signatures match (or at least I infer that from the things I've read so far ).

Awesome article! Delegates made simple for us blue-collar coders. I have ause for delegates in an app but I need a point in the right direction on one problem. I need to pass a delegate as a parameter which you've well illustrated. In addition I want to pass a second parameter to the function receiving the delegate as the first param. That second parameter would be some kind of collection of paramaters for the (unknown) function stamp of the delegate. (confused yet, I am).

Basically what I want to do is write a one-time file handler that when the file is handled successfully calls the delegate with whatever parameters. It shouldn't care what the function is or the parameters that's up to the caller. So...e.g.

The basic problem with function pointers and pointers to member functions is that, neither of them is type-safe
I don't agree - it is entirely possible(with the use of templates) to have a delegate like event system that hooks into C++ member functions that IS type safe.
I have implemented this in the VCF and it works quite nicely:

the above code uses NO type casting internally to bind the function pointer to the instance variable passed in. Of course the function prototypes tend to be more rigid than in the .NET delegate system, but given that they are pretty useful and typesafe.

Worth a lot more than 2 cents, Jim, I tell ya
Well, what can I say. My OOP awareness is not good enough for me to even make a fight out of a technical argument with you. I guess a fellow who knows enough OOP will always find ways to overcome language level difficulties and constraints.

Nish - Native CPian wrote:Worth a lot more than 2 cents,
Yes well sometimes I wonder...I just wish I could more people interested in using/developiong the VCF. It is simply too big a job for one person, which makes me kind of sad since it is extraordinarily feature rich API, but such is life...

Yes well sometimes I wonder...I just wish I could more people interested in
using/developiong the VCF. It is simply too big a job for one person, which
makes me kind of sad since it is extraordinarily feature rich API, but such is
life...

Yeah, and I guess some people may just move on to other areas like 3d-graphics design?

I have redone nearly the entire article. The original was written for beta 2 and covered only C#. This time I have made it more exhaustive and thorough. It explains what .NET delegates are and how they are implemented in MC++ and C#. The sample programs are given in both languages.

I hope this is a major improvement over my previous effort. I have spend all of today on this article. Funnily 75% of my time went into language refining and simplification of technical concepts.

I also hope the editors will find it formatted correctly so that they can move it without too much extra work. It's now in the .NET section and I hope Jambo will be pleased with this

Nish - Native CPian wrote:I also hope the editors will find it formatted correctly so that they can move it without too much extra work. It's now in the .NET section and I hope Jambo will be pleased with this

Oh sure, more work for me I don't mind really its one more article to edit before I tackle those 10 page essays near the top of the list

(1) This program was compiled and ran fine using .Net beta 2 C# compiler.
Can you be more specific as to the error you got?

(2) I am not Nemesh. I don't know why you have this confusion. I remember how previously too, you referred to me as Nemesh. There is a guy called Nemesh Singh who also writes articles on CP. Perhaps you meant that guy.

The thing I like in this delegate intro (and I've read quite a few) is that the author compares delegates to function pointers in "good old C" (not C++). In all other texts on delegates (including MSDN, Professional programming in C#, Inside C#, etc.), they talk about function pointers in C++, and totaly ignore function objects, which are very flexible and inteligent replacement for function pointers. When someone says: "Delegates in C# are much better than function pointers in C++", I just wish to scream "What about function objects?".