Click here to Skip to main content
Click here to Skip to main content

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

By , 12 Jun 2002
 

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.

//delegate declaration using Managed C++
__delegate String* DelegateAbc(); 
//delegate declaration using C#
public delegate String DelegateAbc();

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.

//binding delegates using MC++
DelegateAbc *d1 = new DelegateAbc(t1,&Test::TestAbc); //instance method
DelegateAbc *d2 = new DelegateAbc(0,&Test::TestStatic); //static method
//binding delegates using C#
DelegateAbc d1 = new DelegateAbc (t1.TestAbc); //instance method
DelegateAbc d2 = new DelegateAbc (Test.TestAbc); //static method

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.

//multi-cast delegate using MC++
d1 = static_cast<DelegateAbc*> (Delegate::Combine(d1,
         new DelegateAbc(t1,&Test::TestAbc)));
//multi-cast delegate using C#
d1 = d2 + new DelegateAbc (Test.TestAbc); //using the + operator
d1 += new DelegateAbc (Test.TestAbc); //using the += operator 

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.

//removing a delegate from a multi-cast delegate - MC++
d1 = static_cast<DelegateAbc*>(Delegate::Remove(d1,d2));
//removing a delegate from a multi-cast delegate - C#
d1 = d1 - d2; //using the - operator
d1 -= d3; //using the -= operator

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.

//invoking a delegate with MC++
d1->Invoke("4"); //passing a string as argument
d2->Invoke(); //no arguments
//invoking a delegate with C#
d1("4");  //passing a string as argument
d2(); //no arguments

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.

/* Managed C++ Sample */

#include "stdafx.h"
#using <mscorlib.dll>
using namespace System;

__delegate String* DelegateAbc(String* txt);

__gc class Test
{
public:
    String* TestAbc(String* txt)
    {
        Console::WriteLine(txt);
        return "Hello from TestAbc";
    }
    static String* TestStatic(String* txt)
    {
        Console::WriteLine(txt);
        return "Hello from TestStatic";
    } 
};
int wmain(void)
{ 
    Test *t1 = new Test();
    DelegateAbc *d1 = new DelegateAbc(t1,&Test::TestAbc);
    Console::WriteLine(d1->Invoke("First call"));
    d1 = new DelegateAbc(0,&Test::TestStatic);
    Console::WriteLine(d1->Invoke("Second call")); 
    return 0;
}
/* C# Sample */

using System;

class DelegateDemo
{
    delegate String DelegateAbc(String txt);

    public String TestAbc(String txt)
    {
        Console.WriteLine(txt);
        return "Hello from TestAbc";
    }
    public static String TestStatic(String txt)
    {
        Console.WriteLine(txt);
        return "Hello from TestStatic";
    } 
    static void Main()
    {
        DelegateDemo t1 = new DelegateDemo();
        DelegateAbc d1 = new DelegateAbc(t1.TestAbc);
        Console.WriteLine(d1("First call"));
        d1 = new DelegateAbc(DelegateDemo.TestStatic);
        Console.WriteLine(d1("Second call")); 

    }
}

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.

/* Managed C++ Sample */

#include "stdafx.h"
#using <mscorlib.dll>
using namespace System;

__delegate void DelegateAbc();

__gc class Test
{
public:
    void TestAbc()
    {
        Console::WriteLine("This is from TestAbc"); 
    }
    static void TestStatic()
    {
        Console::WriteLine("This is from the static method"); 
    } 
};
int wmain(void)
{ 
    Test *t1 = new Test();
    DelegateAbc *d1 = new DelegateAbc(t1,&Test::TestAbc); 
    DelegateAbc *d2 = new DelegateAbc(0,&Test::TestStatic);
    d1 = static_cast<DelegateAbc*> (Delegate::Combine(d1,d2));
    d1->Invoke();
    d1 = static_cast<DelegateAbc*>(Delegate::Remove(d1,d2));
    Console::WriteLine();
    d1->Invoke();
    return 0;
}
/* C# Sample */

using System;

class DelegateDemo
{
    delegate void DelegateAbc();

    public void TestAbc()
    {
        Console.WriteLine("This is from TestAbc"); 
    }
    public static void TestStatic()
    {
        Console.WriteLine("This is from the static method"); 
    } 

    static void Main()
    {
        DelegateDemo t1 = new DelegateDemo();
        DelegateAbc d1 = new DelegateAbc(t1.TestAbc); 
        DelegateAbc d2 = new DelegateAbc(DelegateDemo.TestStatic);
        d1 = d1+d2;
        d1();
        d1 -= d2;
        Console.WriteLine();
        d1();
    }
}

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.

/* Managed C++ Sample */

#include "stdafx.h"
#using <mscorlib.dll>
using namespace System;

__delegate int DelegateAbc(int);

__gc class Test
{
public:
    int SquareMe(int i)
    {
        return i*i;
    }
    int CubeMe(int i)
    {
        return i*i*i;
    } 
    void ShowResult(DelegateAbc* d, String* s,int i)
    {
        Console::WriteLine("{0} of {1} is {2}",s,
            i.ToString(),d->Invoke(i).ToString());
    }
};
int wmain(void)
{ 
    Test *t = new Test();
    t->ShowResult(new DelegateAbc(t,&Test::SquareMe),"Square",7);
    t->ShowResult(new DelegateAbc(t,&Test::CubeMe),"Cube",7);
}
/* C# Sample */

using System;

class DelegateDemo
{
    delegate int DelegateAbc(int i);
    
    public    int SquareMe(int i)
    {
        return i*i;
    }
    public int CubeMe(int i)
    {
        return i*i*i;
    } 
    void ShowResult(DelegateAbc d, String s,int i)
    {
        Console.WriteLine("{0} of {1} is {2}",s,i,d(i));
    }

    static void Main()
    {
        DelegateDemo t = new DelegateDemo();
        t.ShowResult(new DelegateAbc(t.SquareMe),"Square",7);
        t.ShowResult(new DelegateAbc(t.CubeMe),"Cube",7);
    }
}

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Nish Sivakumar

United States United States
Nish is a real nice guy who has been writing code since 1990 when he first got his hands on an 8088 with 640 KB RAM. Originally from sunny Trivandrum in India, he has been living in various places over the past few years and often thinks it’s time he settled down somewhere.
 
Nish has been a Microsoft Visual C++ MVP since October, 2002 - awfully nice of Microsoft, he thinks. He maintains an MVP tips and tricks web site - www.voidnish.com where you can find a consolidated list of his articles, writings and ideas on VC++, MFC, .NET and C++/CLI. Oh, and you might want to check out his blog on C++/CLI, MFC, .NET and a lot of other stuff - blog.voidnish.com.
 
Nish loves reading Science Fiction, P G Wodehouse and Agatha Christie, and also fancies himself to be a decent writer of sorts. He has authored a romantic comedy Summer Love and Some more Cricket as well as a programming book – Extending MFC applications with the .NET Framework.
 
Nish's latest book C++/CLI in Action published by Manning Publications is now available for purchase. You can read more about the book on his blog.
 
Despite his wife's attempts to get him into cooking, his best effort so far has been a badly done omelette. Some day, he hopes to be a good cook, and to cook a tasty dinner for his wife.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberMember 844341120-Mar-13 2:42 
cheers
QuestionExample won't compile.memberbadymojoy18-Jul-12 2:40 
10 years later...
 
It looks like a great article, Nish. Unfortunately I'm so lost with Delegates at the moment I don't know how to fix this problem I've run into. In program 1, C#,
 
static void Main()
    {
        DelegateDemo t1 = new DelegateDemo();
        DelegateAbc d1 = new DelegateAbc(t1.TestAbc);
        Console.WriteLine(d1("First call"));
        d1 = new DelegateAbc(DelegateDemo.TestStatic);
        Console.WriteLine(d1("Second call")); 
 
    }
 
4 Compile Errors in VS 2010, signatures don't match TestStatic or TestAbc, and neither take just 1 argument. Cry | :((
AnswerRe: Example won't compile.mvpNish Sivakumar18-Jul-12 3:09 
Interesting, I just copy-pasted the code from example 1 into a test console project in VS 2010 and it compiled fine.
 
using System;
 
class DelegateDemo
{
    delegate String DelegateAbc(String txt);
 
    public String TestAbc(String txt)
    {
        Console.WriteLine(txt);
        return "Hello from TestAbc";
    }
    public static String TestStatic(String txt)
    {
        Console.WriteLine(txt);
        return "Hello from TestStatic";
    } 
    static void Main()
    {
        DelegateDemo t1 = new DelegateDemo();
        DelegateAbc d1 = new DelegateAbc(t1.TestAbc);
        Console.WriteLine(d1("First call"));
        d1 = new DelegateAbc(DelegateDemo.TestStatic);
        Console.WriteLine(d1("Second call")); 
 
    }
}
Regards,
Nish
My technology blog: voidnish.wordpress.com

GeneralThank you [modified]memberahmedmemo31-Dec-11 10:58 
Thank you for this article

modified 31-Dec-11 17:05pm.

GeneralVery nice articlememberReuven22212-Jan-09 23:28 
Very good article about delegates.
It explains the delegates very well with clear and easy samples.
Thanks Smile | :)
GeneralDelgates are Type safesussBabu Aboobacker E.I12-Dec-04 16:55 
Hi Nish,
 
Thank you for posting such a simple but useful article.Rose | [Rose]
 
Can you please explain what you mean by "Unlike function pointers, delgates type-safe".Confused | :confused:
 
P.S "The Delegate class is the base class for delegate types" and MulticastDelegate is derived from Delgate.Isn't it?(Ref: Conclusion section).Cool | :cool:
 
Thanking You
Babu
GeneralRe: Delgates are Type safememberSam Hobbs4-Mar-12 17:59 
I also do not understand the implication that function pointers are not type-safe. Function pointers must be of a specified type, so as far as I know, function pointers are type-safe.
GeneralParameter Listsmembercodehacker3826-Feb-04 1:11 
Hey Nish,
 
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.
 
bool fooFileHandler(Delegate fooSomeFunction, ??? SomeParameters)
.
.
.
if (!fooFileHandler(myFunction, itsParameters)
{
// tell the user they don't have access to that file, etc.
// return to calling function, etc.
}
 
Does this make sense? Smile | :)
 
Thanks
GeneralRe: Parameter ListssussKubko2-Nov-04 10:11 
I doubt if it is possible in such simple way. C# simply isn't LISP. Probably reflexion may help.
GeneralEasier than Delegate::Combine:memberJeremy Kimball26-Jul-02 11:40 
Don't know if anyone's paying attention to this thread anymore, but:
 
Managed C++ has overloaded a few operators of it's own, making delegates act far more like C# delegates.
 
The following two chunks of code work identically:
 
__delegate int SomeDelegate(int i, int j);
__gc class SomeClass { ... };
 
// combine two delegates
SomeClass *psc1 = new SomeClass();
SomeClass *psc2 = new SomeClass();
SomeDelegate *pscd = static_cast(
Delegate::Combine(
new SomeDelegate( psc1, &SomeClass::SomeMethod ),
new SomeDelegate( psc2, &SomeClass::SomeMethod )
)
);
pscd->Invoke(1);
 
// combine 2 other delegates - shorthand style
SomeClass *psc3 = new SomeClass();
SomeDelegate *pCombo = new SomeDelegate(psc3, &SomeClass::SomeMethod);
pCombo += new SomeDelegate(psc3, &SomeClass::SomeMethod);
pCombo(1);
GeneralHmmmmemberJim Crafton13-Jun-02 9:10 
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:
class MyWindow : public VCF::Window {
  
  MyWindow () {
    panel1 = new VCF::Panel();
    //here the event handler:
    EventHandler* handler = 
                   new MouseEventHandler<MyWindow>( this,
                                                  MyWindow::onPanel1MouseDown, 
                                                  "onPanel1MouseDown" );
 
    panel1->addMouseDownHandler( handler );
    add( panel1, AlignClient );
  }
 
  void onPanel1MouseDown( MouseEvent* e ) {
 
  }
 
  Panel* panel1;
}
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.
 
Just my 2 cents Smile | :)

GeneralRe: HmmmmemberNish - Native CPian13-Jun-02 14:31 
Jim Crafton wrote:
Just my 2 cents
 
Worth a lot more than 2 cents, Jim, I tell ya Smile | :)
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.
 
Regards
Nish
 


Author of the romantic comedy

Summer Love and Some more Cricket [New Win]

Buy it, read it and admire me Smile | :)

GeneralRe: HmmmmemberJim Crafton13-Jun-02 18:55 
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...
 

GeneralRe: HmmmmvpNish Sivakumar18-Jul-12 3:11 
Jim Crafton wrote:

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? Roll eyes | :rolleyes:
Regards,
Nish
My technology blog: voidnish.wordpress.com

GeneralThoroughly revamped - Jun 13th 2002memberNish - Native CPian13-Jun-02 1:52 
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 Smile | :)
 
Regards
Nish

 


Author of the romantic comedy

Summer Love and Some more Cricket [New Win]

Buy it, read it and admire me Smile | :)

GeneralRe: Thoroughly revamped - Jun 13th 2002subeditorJames T. Johnson13-Jun-02 9:50 
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 Roll eyes | :rolleyes: I don't mind really its one more article to edit before I tackle those 10 page essays near the top of the list Poke tongue | ;-P
 
James
GeneralRe: Thoroughly revamped - Jun 13th 2002memberNish - Native CPian13-Jun-02 14:33 
James T. Johnson wrote:
those 10 page essays near the top of the list
 
There are 10 page articles there, eh?
Wow!
 
Nish Eek! | :eek:
 


Author of the romantic comedy

Summer Love and Some more Cricket [New Win]

Buy it, read it and admire me Smile | :)

GeneralRe: Thoroughly revamped - Jun 13th 2002subeditorJames T. Johnson13-Jun-02 18:40 
Well I don't know if they're exactly 10 pages; but they're pretty damn long (and daunting) Big Grin | :-D
 
James <-- MSG #2000 Jig | [Dance]
GeneralRe: Thoroughly revamped - Jun 13th 2002memberNish - Native CPian13-Jun-02 18:42 
James T. Johnson wrote:
Well I don't know if they're exactly 10 pages; but they're pretty damn long (and daunting)
 
Heheheh. Good luck, anyway Smile | :)
 
James T. Johnson wrote:
James <-- MSG #2000
 
Oh! Er, congratulations Smile | :)
 

 


Author of the romantic comedy

Summer Love and Some more Cricket [New Win]

Buy it, read it and admire me Smile | :)

GeneralPlease, don't make any article with Beta 1!memberMN18-Oct-01 15:18 
Thanks.
 
P.S.
Update your development tools, evaluate a new version and bring something what is not in the MSDN described (or confused).
GeneralYaar, does not work in .Net Beta 2memberAmita Buch16-Oct-01 12:10 
I have now moved to .Net Beta 2 and most of your programs don't work any more.
 
Nemesh when will you change these
GeneralIt works fine in .Net Beta 2.......memberNish [BusterBoy]16-Oct-01 17:06 
Hello Amita Buch
 
(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.
 
Nish
GeneralFunction pointers vs function objects vs delegatesmemberNemanja Trifunovic16-Oct-01 5:34 
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?". Roll eyes | :rolleyes:
 
I vote pro drink Beer | [beer]
AnswerRe: Function pointers vs function objects vs delegatesmvpNish Sivakumar18-Jul-12 3:12 
11 years later, but thank you Smile | :)
Regards,
Nish
My technology blog: voidnish.wordpress.com

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130617.1 | Last Updated 13 Jun 2002
Article Copyright 2001 by Nish Sivakumar
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid