< C++ .NET Properties 7 | Main | C++ .NET Delegates & Events 2 >


 

 

Delegates and Events 1

generate, fire and handle the events...

 

 

  1. What Are Delegates?

  2. What Are Function Pointers?

  3. What Do Delegates Do?

  4. Defining Delegates

  5. Implementing Delegates

------------Next------------------

  1. Calling Static Member Functions Using Delegates

  2. Calling Non-Static Member Functions Using Delegates

  3. Using Multicast Delegates

  4. What Are Events?

  5. Implementing an Event Source Class

  6. How Does the event Keyword Work?

  7. Implementing an Event Receiver

  8. Hooking It All Together

  9. Quick Reference

  10. More on Delegates and Events

  11. Managed Extensions for C++: the __delegate keyword

  12. Managed C++ .NET: the delegate keyword

  13. The Unbound Delegates

  14. More on Defining and Using Delegates

  15. Associating Delegates to Members of a Value Class

  16. Associating Delegates to Unmanaged Functions

  17. Composing Delegates

  18. Passing a Delegate^ to a Native Function Expecting a Function Pointer

  19. The Generic Delegates (C++)

 

Delegates and events are extremely powerful and important constructs in the Microsoft .NET Framework. Events in particular are used widely in GUI programs as a means of communicating between components, but both delegates and events can be used to good effect in non-GUI code.

 

What Are Delegates?

 

The function pointer mechanism in C and C++ has been used by programmers for many years, and it’s a very useful way of implementing mechanisms such as event handlers. Unfortunately, function pointers are a C++ language feature, so they’re of no use in the .NET environment, where features need to be accessible from many languages. Delegates are the .NET equivalent of function pointers, and they can be created and used from any .NET language. They can be used by themselves, and they also form the basis for the .NET event mechanism discussed in the second part of this module.

 

What Are Function Pointers?

 

A normal pointer lets you access a variable through the address it contains. A function pointer lets you execute a function using the address of the routine. In exactly the same way that you can use a pointer to hold the addresses of different variables, you can use the same function pointer to invoke different functions. And in the same way that normal pointers must have a type associated with them (so that you can only point at doubles with a double*, for example), function pointers must have a function signature associated with them. The following line of code shows how you declare a function pointer in C++:

long (*pf)(int, int);

The code declares a function pointer called pf, which can be used to invoke any function that takes two int parameters and returns a long. The following function prototype has the right signature:

long func1(int, int);

You can invoke the function indirectly like this:

pf = func1;         // assign address of func1 to pf

long l = pf(3,4);   // invoke func1() through pf

Remember that in C++, the name of a function without any parentheses evaluates to its address, so the first line takes the address of the function and stores it in pf. The second line uses pf to invoke the function. You can use a function pointer to invoke any function that matches its signature, and that’s what makes function pointers useful for event handling. You can define a function pointer to represent the event handler and then hook up the actual function to the pointer later.

 

What Do Delegates Do?

 

A delegate is a class that lets you invoke one or more methods that have a particular signature. Here’s a simple example to show when you might want to use a delegate. Imagine that we want to be able to perform operations on numbers by passing a number into a function and getting a transformed value back, like this:

double d = 3.0;

 

double result = square(d);

result = cube(d);

result = squareRoot(d);

result = tenToThePowerOf(d);

In each case, we are calling a function that has the same signature: one that takes a double and returns a double as its result. With delegates, we can define a mechanism that will let us call any of those methods because they all have the same signature. Not only can we call any of the four methods above, but we can also define other methods and can call them through the delegate, provided that the signature matches. This makes it possible for one class or component to define a delegate, and for other classes to attach functions to the delegate and use it. You’ll see examples of this use of delegates later in the module when we cover events. In this case, we want to use the delegate to call one method at a time, but it’s possible to attach more than one function to a delegate. All the functions will get called in order when the delegate is invoked. The .NET Framework defines the System::Delegate class as the base for delegates that call a single method and System::MulticastDelegate as the base for delegates that can call more than one method. All delegates in managed C++ are multicast delegates.

 

Defining Delegates

 

This exercise uses the numerical operations example from the previous section to show you how to create and use a simple delegate in managed C++ code.

 

1.        Open Microsoft Visual Studio/C++ .NET if it isn’t already opened and create a new CLR Console Application project named Delegate.

 

C++ .NET CLR Console Application new project creation

 

2.        Open the Delegate.cpp source file and add the definition of a delegate to the top of the file, immediately after the using namespace System; line.

delegate double NumericOp(double);

 

Delegate.cpp main file code addition

 

The delegate keyword is used to define a delegate. It might look as though this is a function prototype for a function named NumericOp, but it’s actually defining a delegate type that inherits from System::MulticastDelegate. This delegate, named NumericOp, can be bound to any function that takes one double as an argument and returns a double.

 

Implementing Delegates

 

Now that you have defined a delegate, you can write code to use it to call functions. One of the rules for using delegates is that you can only use a delegate to call functions that are members of managed C++ classes; you can’t use a delegate to call a global function or a function that’s a member of an unmanaged C++ class.

 

Calling Static Member Functions Using Delegates

 

Let’s start by looking at the simplest case: calling static member functions using a delegate.

 

3.        All the functions we want to call need to be static members of a class, so add a class to your source code file, above the main function, that looks like this:

ref class Ops

{

   public:

         static double square(double d)

         {  return d*d;  }

};

The class definition has to use ref to make it a managed class, and it contains one public static method, which simply takes a number and returns its square.

 

4.        Create a delegate in the main function of the program, as shown here:

// Declare a delegate

NumericOp^ pOp = gcnew NumericOp(&Ops::square);

 

Creating a delegate in the main() function

 

 

When you declared the delegate, you created a new reference type named NumericOp, so you can now create a NumericOp object. The argument is the address of the function that is to be associated with the delegate, so you use the & operator to specify the address of Ops::square. The object pointed to by pOp is now set up so that it will call the square function when it is invoked, and it will take exactly the same arguments (and return the same type) as Ops::square. You can’t change the function that a delegate invokes once it’s been created. In this respect, delegates differ from C++ function pointers. Every delegate has an Invoke method that you use to call the function that has been bound to the delegate. Invoke will take the same arguments and return the same type as the function being called.

 

5.        Add the following lines to use pOp to call the square function:

// Call the function through the delegate

double result = pOp->Invoke(3.0);

Console::WriteLine(L"Result is {0}", (Object^)(result));

 

Adding codes to use pOp to call the square function

 

6.        You can now easily create another static member, create a delegate, and call the function. Test this out by adding a second static member to the Ops class named cube.

static double cube(double d)

return d*d*d; }

 

Adding codes of the static member to the Ops class named cube

 

7.        Create another delegate in the same way as the first, but this time, pass it the address of the cube function in the constructor.

// Declare a second delegate

NumericOp^ pOp2 = gcnew NumericOp(&Ops::cube);

Adding codes to create another delegate

 

8.        When you call Invoke on this delegate, it will call the cube function for you, as seen in the following code. Add the following code.

// Call the function through the delegate

double result2 = pOp2->Invoke(3.0);

Console::WriteLine(L"Result of cube() is {0}", (Object^)(result2));

 

Calling the function through the delegate using Invoke()

 

9.        Build and run your project. The following output should be expected.

 

Delegate program output sample

 

 

 

Part 1 | Part 2 | Part 3 | Part 4 | Part 5 | Part 6 | Part 7

 


 

< C++ .NET Properties 7 | Main | C++ .NET Delegates & Events 2 >