Download as pdf or txt
Download as pdf or txt
You are on page 1of 5

Basic Instincts

Implementing Callback Notifications


Using Delegates
Ted Pattison

Contents
Callback Notifications
Introducing Delegates
Creating a Delegate Object
Binding a Delegate to an Instance Method
Conclusion

Delegates are a critical aspect of the Microsoft® .NET Framework and are required learning for many
programmers. They will take some time to master, but once you learn how to program with delegates, you'll
be thrilled with the possibilities they offer.
Let's dig in. First, you should know that events in the .NET Framework are layered on top of delegates. When
you use an event-driven application framework such as Windows® Forms or ASP.NET, your knowledge of
delegates will make you a much stronger developer. Delegates provide the primary means in .NET for
executing a method on a secondary thread in an asynchronous fashion. Therefore, delegates open the door to
multithreading.
Delegates represent a .NET innovation intended to address application designs that involve callback
notifications. In this month's column I am going to show you how to implement callbacks using delegates.
However, before I jump into programming with delegates, I want to explain why and how callback
notifications have been implemented in the past.

Callback Notifications
Application software is often designed around the notion of callbacks. The use of callbacks is a programming
technique in which one part of an application sends out notifications to alert other parts of the application
whenever something interesting has occurred. To be more specific, a callback is a method that is implemented
by one or more handlers and is then executed by a notification source.
Most programmers who use Visual Basic® should be familiar with the concept of callbacks because of the
manner in which Visual Basic has always supported event handling. An event handler is a simple example of a
callback method. When you write an event handler for the Click event of a command button, you are really
writing the implementation for a callback method. However, you are not required to explicitly call the event
handler. Instead, the Form class of the Windows Forms framework acts as a notification source because it
automatically executes your event handler method at exactly the right time.
It can be helpful to make an analogy to something that might occur in everyday life to fully appreciate the
value of a design based on callbacks. Imagine your boss has just assigned you a task. Assume that this task will
take several hours to complete and that your boss wants to know the minute that you are finished. It would
probably become very annoying if your boss called you on the phone every few minutes to ask if you had
finished the task. It would be far more reasonable for you to make the following suggestion to your boss.
"Don't call me; I'll call you when I'm finished."
As long as you notify your boss the moment you've completed the task, your boss can react and take whatever
actions are appropriate in a timely fashion. As you can imagine, the use of a callback in this particular scenario
is going to be more efficient (and far less annoying) than having your boss poll you every few minutes to see if
you've finished your work.
If you are using Visual Basic .NET or an earlier version such as Visual Basic 6.0, you can implement a callback
design using an interface. For example, you can use an interface to define a callback method. Then you can
create a listener class that implements this interface. In doing so, your listener class will provide an
implementation for the handler method defined by the interface. Finally, you can design another object to act
as a notification source. The notification source object should be designed to track an interface-based
reference to a listener object. Once the notification source object has a reference to the listener object, it can
execute the handler method whenever it needs to send out callback notification.
C++ programmers have been implementing callbacks in their applications long before the .NET Framework
came along. However, a callback in C++ is often implemented without the use of an interface. Instead, it can
be implemented using something called a function pointer. While it's not important for you to know the
low-level details of how to program using function pointers, it's valuable to understand them at a higher level
because of how much influence they have had on the architecture of the .NET Framework.
A function pointer is an in-memory address that points to a method implementation. At the physical level, a
function pointer points to a set of instructions that represents the executable logic for a method. To use a
function pointer, one part of the application must initialize it to point to a particular method. Another part of
an application can then use this function pointer to execute the method to which it points.
Function pointers are useful in C++ because they provide an efficient way to implement a callback between a
notification source and a listener. As long as a notification source can obtain a function pointer that points to
the implementation for a handler method, it can send out notifications in an anonymous fashion.
When it comes to modeling callbacks, however, the use of function pointers offers a few distinct advantages
over the use of interfaces. First, function pointers don't require that a notification source and its listeners agree
on the names of callback methods. Second, the use of function pointers can provide a higher degree of
granularity than interfaces because you can register handlers to receive callbacks on a method-by-method
basis. Function pointers also make it possible to use shared (static) methods in addition to instance methods.

Introducing Delegates
When architects on the .NET team began to design their new framework, they knew they wanted to provide
rich support for implementing callbacks. They weighed the pros and cons of implementing callback methods
with interfaces versus function pointers. In the end, they decided to create a new hybrid technique that
combines the type safety and polymorphism of using interfaces together with the efficiency and flexibility of
using function pointers. This new technique involves an innovation known as a delegate.
A delegate is a special kind of type within the programming model of the .NET Framework. The architects of
the .NET Framework added delegates to provide a convenient binding mechanism to wire up one or more
handler methods to a notification source. As you will see, delegates can be used to implement the same kinds
of callback designs that you can implement using interfaces or function pointers. However, a callback design
based on a delegate often requires less code and provides more features than a callback design based on an
interface or function pointers.
You can define a delegate type in Visual Basic .NET using the Delegate keyword. Each delegate definition you
create must include a type name and the calling signature for a handler method. Here's an example of three
delegate type definitions:

Delegate Sub BaggageHandler()


Delegate Sub MailHandler(ItemID As Integer)
Delegate Function QuoteOfTheDayHandler(Funny As Boolean) As String

As you can see, you define the calling signature of a delegate type using the standard Visual Basic syntax for
method definitions. Like a method, a delegate type must be defined using either the Sub keyword or the
Function keyword. A delegate definition is also similar to a method definition in that it can optionally define a
parameter list and a return value.
When you compile code that contains a delegate type definition, the Visual Basic .NET compiler does some
extra work for you behind the scenes. In particular, the compiler generates a class definition for each delegate
type. The class that is generated for each delegate type is a creatable class that inherits from the
System.Multicast delegate. Figure 1 shows what the preceding three delegate types look like after compilation
from the perspective of ILDASM.EXE.

Figure 1 Delegate Types After Compilation


Take a moment to examine the definition of the delegate type QuoteOfTheDayHandler in Figure 1. You can
see that in addition to creating a class that inherits from MulticastDelegate, the Visual Basic .NET compiler has
also added a public constructor and three public methods named Invoke, BeginInvoke, and EndInvoke.
The Visual Basic .NET compiler adds a public constructor to each delegate definition to make it a creatable
type. The public constructor is the member with the name .ctor. Once you acknowledge that a delegate is a
creatable type, it is easier to understand how it's used in an application. Programming with delegates requires
creating delegate objects from delegate types. Furthermore, each delegate object must be created in such a
way so that it gets initialized to point to a target method implementation. In just a moment, you will see how
to do this by writing the code to create a delegate object that gets bound to a target handler method.
In addition to a public constructor, the compiler automatically adds a public method named Invoke whenever
it generates a delegate type definition. The Invoke method is supplied so that a notification source can
execute the target handler method to which the delegate object is bound. When you call Invoke on a delegate
object, it simply forwards the call to the target handler method.
The calling signature for the Invoke method of QuoteOfTheDayHandler matches the calling signature of the
delegate type itself. This is always the case with a delegate type definition. The compiler will always generate
an Invoke method whose calling signature matches that of the containing delegate type. Any input parameters
that you pass in a call to Invoke will be forwarded in the call to the target handler method. If the target
handler method has any output parameters or a return value, these values will be returned to the caller of the
delegate object's Invoke method.
You have probably also noticed that the definition for type QuoteOfTheDayHandler contains two additional
methods generated by the compiler, named BeginInvoke and EndInvoke. These two methods provide a basis
for executing a delegate object's target handler method on a separate thread in an asynchronous fashion. The
fact that delegates can be used to execute certain tasks asynchronously makes them that much more powerful.
However, I will defer a discussion of how to use BeginInvoke and EndInvoke to execute methods
asynchronously for a future column.

Creating a Delegate Object


When you want to create a delegate object, you typically use the New operator followed by the name of the
delegate type. When you create a delegate object using the New operator, you must provide the information
that's needed to bind the new delegate object to the implementation of a target handler method. Let's step
through an example using the QuoteOfTheDayHandler delegate.
Before you can create a delegate object, you must determine what handler method you'd like to bind it to. A
handler method can be either a shared method or an instance method. Keep in mind that the name of the
handler method doesn't really matter. The only requirement is that a handler method be defined with a calling
signature that matches the delegate type. For the first example, imagine you have written a class named
JennysHandlers that contains a shared method that was written to be a handler method for the
QuoteOfTheDayHandler delegate type, as shown in the following code:

Class JennysHandlers
Shared Function GetQuote(ByVal Funny As Boolean) As String
'*** custom handler implementation
End Function
End Class

As you can see, the shared method GetQuote has the correct calling signature to be used with the
QuoteOfTheDayHandler delegate. When you want to create a delegate object, you can use the New operator
and pass a parameter value with the information that allows the CLR to bind the new delegate object to a
target method implementation. The way to accomplish this in Visual Basic .NET is by using the AddressOf
operator followed by the method name. For example, you can create a delegate object that is bound to the
GetQuote method using the following code:

Dim handler1 As QuoteOfTheDayHandler


handler1 = New QuoteOfTheDayHandler(AddressOf JennysHandlers.GetQuote)

Note that the Visual Basic .NET compiler will generate a compile-time error if the calling signature of the
GetQuote method does not match the calling signature of the delegate type QuoteOfTheDayHandler. These
compile-time checks are what makes programming with delegates far less error-prone than programming
with function pointers. The strongly typed nature of delegates ensures that a notification source and its
handler methods all agree on a particular calling signature.
You might remember the AddressOf operator from previous versions of Visual Basic. This operator was
introduced in Visual Basic 5.0 to provide a means for passing actual function pointers to low-level functions in
the Win32® API. However, the role of AddressOf is different in Visual Basic .NET. Its primary purpose is now to
initialize delegate objects.
Note that the Visual Basic .NET language provides a convenient shorthand syntax for creating a delegate
object. The previous code sample can be rewritten more concisely:

Dim handler1 As QuoteOfTheDayHandler


handler1 = AddressOf JennysHandlers.GetQuote
As you can see, the call to the New operator can be omitted for convenience. In this example, the Visual Basic
.NET compiler is designed to create a new object from the QuoteOfTheDayHandler delegate type and assign a
reference to this delegate object back to the variable handler1. What you should see is that whenever the
compiler expects a delegate object of type QuoteOfTheDayHandler, you can simply pass the following
expression:

AddressOf JennysHandlers.GetQuote

The Visual Basic .NET compiler expands this expression into code that creates a new QuoteOfTheDayHandler
object and binds it to the handler method GetQuote. While the longer syntax might make what's actually
going on more obvious, you might find the shorthand syntax more concise and easier to write.
Now that you know how to bind a new delegate object to a handler method, it's time to see how to execute
the handler method that the delegate object is bound to. You can execute the handler method by simply
calling the Invoke method on the delegate object, as shown in the following code:

'*** create and bind delegate object


Dim handler1 As QuoteOfTheDayHandler
handler1 = AddressOf _
JennysHandlers.GetQuote

'*** execute target method


Dim quote As String = _
handler1.Invoke(True)

Note that the call to Invoke in this example requires a single parameter of type Boolean and has a return value
based on the String type. When you call Invoke on the delegate object, it forwards the call by executing the
target handler method GetQuote. You should also see that the call to Invoke returns the same value that was
returned from the call to GetQuote.
Visual Basic .NET also provides you with another convenient shorthand syntax when programming delegates.
You have the option of omitting the call to the delegate's Invoke method. If you don't provide an explicit call
to Invoke, the Visual Basic .NET compiler will automatically add the call for you. Look at the following lines of
code:

'*** this code


Dim quote1 As String = handler1.Invoke(True)

'*** is the same as this code


Dim quote2 As String = handler1(True)

As you can see, the call to Invoke can be made either explicitly or implicitly. When you replace the syntax
handler1.Invoke(True) with the syntax handler1(True), the Visual Basic .NET compiler automatically adds the
call to Invoke for you. Therefore, you can simply treat a reference variable or a field based on a delegate type
as if it were the name of an actual method.
Whether you make calls to the Invoke method explicitly or implicitly comes down to a stylistic preference on
your part. It has no effect on your code once it is compiled. Some programmers prefer explicit calls to Invoke
because they feel it makes their code easier to read. Others prefer implicit calls to Invoke because this results
in a little less typing.
It is interesting to note that the C# compiler doesn't allow for explicit calls to a delegate's Invoke method.
Instead, it requires that programmers use the implicit style in which a delegate reference is treated as though
it were an actual method name. The C# compiler always adds the call to Invoke during compilation. If you are
planning to switch back and forth between Visual Basic .NET and C#, you might consider using the implicit
style of calling Invoke because it promotes greater consistency across languages.

Binding a Delegate to an Instance Method


Up to this point you have seen how to bind a delegate object to a shared method. As you already know, this is
accomplished using the AddressOf operator followed by the class name together with the shared method
name. However, it's also possible to bind a delegate to an instance method. Let's look at a code example so
you can see how binding a delegate object to an instance method differs from binding to a shared method.
Instance methods are different from shared methods because they must execute within the context of an
object created from the class in which they are defined. Therefore, binding a delegate object to an instance
method requires the delegate object to track a target object in addition to tracking its target method
implementation. After all, it would not be possible for a delegate object to execute an instance method unless
it knew what object to use for the method's execution context.
In order to create a delegate object that is bound to an instance method, you must start by creating or
acquiring a reference to an object created from the class that defines the instance method. For example,
suppose you have created a class named JerrysQuotes that contains an instance method named NextQuote, as
shown in this code snippet:

Class JerrysQuotes
Function NextQuote(ByVal Funny As Boolean) As String
'*** implementation
End Function
End Class

In order to bind a delegate object to the instance method NextQuote, you must first create an object from the
class JerrysQuotes. Once you have acquired an object reference, you can create and bind a delegate object to
one of its instance methods. You do this by using the AddressOf operator together with the object reference
followed by the method name:

Dim quotes As New JerrysQuotes()


Dim handler2 As QuoteOfTheDayHandler = AddressOf quotes.NextQuote

The difference between this example and the previous one involving a shared method is that the delegate
object must now keep track of the target object. Figure 2 reveals some of the private implementation details
of a delegate object. As you can see, every delegate object contains a private field that holds a function
pointer to a method implementation. Delegate objects that are bound to an instance method also track a
reference to a target object that will be used when the target instance method is executed.

Figure 2 Implementing Delegate Objects


When you think of how things work at a lower level, a delegate object is really nothing more than a friendly,
type-safe wrapper around a function pointer. However, when you think about delegates at a higher level, it's
important to see that they open up many possibilities when it comes to designing an application that requires
callbacks. Delegates provide a flexible and efficient way to implement a loosely coupled design in which a
notification source must send notifications to a set of handler methods.

Conclusion
This month's column has provided you with an introductory look at delegates. As you have seen, delegates
were introduced in the .NET Framework to assist designers and programmers with implementing callback
notifications. Delegates provide a hybrid technique for implementing callbacks that combine the type safety of
using interfaces together with the efficiency and flexibility of using function pointers.
Now you have seen what is required to define a delegate type and how to create a delegate object and
initialize it to bind to either a shared method or an instance method. You've also seen how to fire the handler
method by calling the Invoke method supplied by the delegate.
In a future column I'll continue this discussion of delegates and will show you how to create a loosely coupled
design using a custom delegate type in which a class has been designed to send out callback notifications. I'll
also explain how delegates seamlessly support binding a notification source to multiple handler methods
through a feature known as multicasting.

You might also like