Delegates The Visual Basic .NET Language

98 Try SomeMethod500 Catch e As MyParameterOutOfRangeException ... End Try End Sub What About On Error? Visual Basic 6 did not have exception objects and Try...Catch blocks. Instead, it used the On Error statement to specify a line within the current procedure to which execution should jump if an error occurred. The code at that point in the procedure could then examine the Err intrinsic object to determine the error that had occurred. For compatibility with previous versions, Visual Basic .NET continues to support the On Error and related statements, but they should not be used in new development, for the following reasons: • Structured exception handling is more flexible. • Structured exception handling does not use error codes. Application- defined error codes often clashed with error codes defined by other applications. • Structured exception handling exists at the .NET Framework level, meaning that regardless of the language in which each component is written, exceptions can be thrown and caught across component boundaries. Error handling with the On Error and related statements are not discussed in this book.

2.19 Delegates

A delegate is a programmer-defined type that abstracts the ability to call a method. A delegate-type declaration includes the declaration of the signature and return type that the delegate encapsulates. Instances of the delegate type can then wrap any method that exposes the same signature and return type, regardless of the class on which the method is defined and whether the method is an instance method or shared method of the defining class. The method thus wrapped can be invoked through the delegate object. The delegate mechanism provides polymorphism for methods having the same signature and return type. Delegates are often used to implement callback mechanisms. Imagine a class that will be used by a program you are writing. This class provides some useful functionality, including the ability to call in to a method that you must implement within your program. Perhaps this callback mechanism is provided to feed your program data as it becomes available in the class you are using. One way to achieve this capability is through the use of delegates. Heres how: 1. The writer of the class youre using call it a server class declares a public delegate type that defines the signature and return value of the method that you will implement. 2. The writer of the server class exposes a method for clients of the class to pass in an instance of the delegate type. 3. You implement a method having the appropriate signature and return value. 4. You instantiate a new object of the delegate type. 5. You connect your method to your delegate instance. 6. You call the method defined in Step 2, passing in your delegate instance. 99 7. The server class now has a delegate instance that wraps your method. The class can call your method through the delegate at any time. 8. Depending on the application, it might be appropriate for the writer of the server class to provide a method that allows the client application to disconnect its delegate from the server to stop receiving callbacks. Ex am ple 2- 13 shows an example of this mechanism. Example 2-13. Defining and using a delegate type to implement a callback mechanism This class is defined in the server component. Public Class ServerClass Even though the following declaration looks similar to a method declaration, it is actually a type declaration. It compiles to a type that ultimately derives from the System.Delegate type. The purpose of the method syntax in this declaration is to define the signature and return type of the methods that instances of this delegate type are able to wrap. Public Delegate Sub MessageDelegateByVal msg As String The following is a private field that will hold an instance of the delegate type. The instance will be provided by the client by calling the RegisterForMessages method. Even though this field can hold only a single delegate instance, the System.Delegate class itself is designed such that a delegate instance can refer to multiple other delegate instances. This feature is inherited by all delegate types. Therefore, the client will be able to register multiple delegates, if desired. See the RegisterForMessages and UnregisterForMessages methods in the current class to see how multiple delegates are saved. Private m_delegateHolder As MessageDelegate = Nothing The client calls the RegisterForMessages method to give the server a delegate instance that wraps a suitable method on the client. Public Sub RegisterForMessagesByVal d As MessageDelegate The System.Delegate classs Combine method takes two delegates and returns a delegate that represents them both. The return type is System.Delegate, which must be explicitly converted to the appropriate delegate type. Dim sysDelegate As System.Delegate = _ System.Delegate.Combinem_delegateHolder, d m_delegateHolder = CTypesysDelegate, MessageDelegate End Sub The client calls the UnregisterForMessages method to tell the server not to send any more messages through a particular delegate instance. Public Sub UnregisterForMessagesByVal d As MessageDelegate The System.Delegate classs Remove method takes two delegates. The first is a delegate that represents a list of delegates. The second is a delegate that is to be removed from the list. The return type is System.Delegate, which must be explicitly converted to the appropriate delegate type. Dim sysDelegate As System.Delegate = _ System.Delegate.Removem_delegateHolder, d 100 m_delegateHolder = CTypesysDelegate, MessageDelegate End Sub The DoSomethingUseful method represents the normal processing of the server object. At some point during normal processing, the server object decides that it is time to send a message to the clients. Public Sub DoSomethingUseful ... Some processing has led up to a decision to send a message. However, do so only if a delegate has been registered. If Not m_delegateHolder Is Nothing Then The delegate objects Invoke method invokes the methods wrapped by the delegates represented by the given delegate. m_delegateHolder.InvokeThis is the msg parameter. End If ... End Sub End Class ServerClass This class is defined in the client component. Public Class ClientClass This is the callback method. It will handle messages received from the server class. Public Sub HandleMessageByVal msg As String Console.WriteLinemsg End Sub This method represents the normal processing of the client object. As some point during normal processing, the client object creates an instance of the server class and passes it a delegate wrapper to the HandleMessage method. Public Sub DoSomethingUseful ... Dim server As New ServerClass The AddressOf operator in the following initialization is a little misleading to read. Its not returning an address at all. Rather, a delegate instance is being created and assigned to the myDelegate variable. Dim myDelegate As ServerClass.MessageDelegate _ = AddressOf HandleMessage server.RegisterForMessagesmyDelegate ... This represents other calls to the server object, which might somehow trigger the server object to call back to the client object. server.DoSomethingUseful ... At some point, the client may decide that it doesnt want any more callbacks. server.UnregisterForMessagesmyDelegate End Sub End Class ClientClass Delegates are central to event handling in the .NET Framework. See the next section for more information. 101 Delegates dont provide any capabilities that cant be achieved in other ways. For example, the solution in Ex am ple 2- 13 could have been achieved in at least two ways that dont involve delegates: • The server component could define an abstract base class defining the method to be implemented by the client. The client would then define a class that inherits from the servers abstract base class, providing an implementation for the classs one method. The server would then provide methods for registering and unregistering objects derived from the abstract base class. • The server component could define an interface that includes the definition of the method to be implemented by the client. The client would then define a class that implemented this interface, and the server would provide methods for registering and unregistering objects that expose the given interface. Any of these methods including delegates could be a reasonable solution to a given problem. Choose the one that seems to fit best. Delegates are sometimes characterized as safe function pointers. I dont think that this characterization aids the learning process, because delegates arent any sort of pointer—safe or otherwise. They are objects that encapsulate method access. Delegate objects can invoke methods without knowing where the actual methods are implemented. In effect, this allows individual methods to be treated in a polymorphic way.

2.20 Events