This month's "basic functions" column is based on my previous two columns. In the previous two "basic functions" columns, I discussed delegation-related concepts and programming skills. This document assumes that the reader has read the last two sections of this column and understands the role of delegation in Microsoft. NET Framework. If you have not read the previous two columns, see implementing callback configurations using delegates and implementing callbacks with a multicast delegate. You should also know how to design and compile a simple application that uses a multicast delegate to send callback notifications to a set of handler methods.
You may have programmed the event for several years, but migrating to. NET Framework still requires you to re-check the internal work of the event, because the event in. NET Framework is located at the top layer of the delegate. The more you know about delegation, the more powerful you can control when programming events. Understanding how an event works at a lower level is critical when you start using an event-driven framework (such as Windows Forms or ASP. NET) in the Common Language Runtime Library (CLR. This month, my goal is to help you understand how events work at a lower level.
What is an event?
An event is only a formal software mode in which the notification source calls back one or more processing methods. Therefore, events are similar to interfaces and delegation because they provide methods for designing applications that use callback methods. However, events greatly increase productivity because they are easier to use than interfaces or delegation. The event allows the compiler and Visual Studio. NET ide to do a lot of work behind the scenes.
Events are designed based on the event source and one or more event handlers. The event source can be a class or an object. The event handler is the delegate object bound to the handler method. Figure 1 shows a high-level view of the event source bound to its handler method.
[Img] http://www.microsoft.com/china/msdn/library/netframework/netframework/art/eventfig01.gif#/img
Figure 1 event sources and handlers
Each event is defined based on a specific delegate type. Each event defined by the event source has a private field of the event-based basic delegate type. This field is used to track multicast delegate objects. The event source also provides a public registration method that allows you to register the required number of event handlers.
When you create an event handler (delegate object) and register it in an event source, the event source only appends the new event handler to the end of the list. The event source can then use a private field to call invoke on a multicast delegate. The multicast delegate will execute all registered event handlers in sequence.
The real benefit of an event is that a lot of setup work has been done for you. As you will soon see, the Visual Basic. Net compiler will assist you by automatically adding private delegate fields and Public Registration methods whenever you define events. You will also see that Visual Studio. NET can provide more help through a code generator, which can automatically generate a trunk definition for your processing method.
Program events
Because events in. NET are built on the delegate, their infrastructure details are completely different from how to use things in earlier versions of Visual Basic. However, the Visual Basic. Net Language designers are doing well in keeping the event programming syntax consistent with earlier versions of Visual Basic. In many cases, programming events involves the old syntax you are used. For example, you will use keywords such as event, raiseevent, and withevents, and their behavior methods are almost the same as those in earlier versions of Visual Basic.
Next, let's create an event-based simple callback design. First, I need to use the event keyword to define an event in the class definition. Each event must be defined based on a specific delegate type. The following is an example of customizing the delegate type and the class used to define the event:
Delegate sub largewithdrawhandler (byval amount as decimal)
Class bankaccount
Public event largewithdraw as largewithdrawhandler
'*** Other members omitted
End Class
In this example, the largewithdraw event is defined as an instance Member. In this design, the bankaccount object acts as the event source. If you want the class (rather than the object) to act as the event source, you should use the shared keyword to define the event as a shared Member.
When programming events, it is important to know that the compiler has done a lot of extra work behind the scenes. For example, when you compile the definition of the bankaccount class just shown to the Assembly, what do you think the compiler will do? Figure 2 shows the class definition generated by the Intermediate Language disassembly program ildasm.exe. This view shows you without reservation how much work the Visual Basic. Net compiler has done behind the scenes to help you.
Figure 2 Definition of classes in ildasm
When you define an event, the compiler generates four members in the class definition. The first member is a private field based on the delegate type. This field is used to trace references to the delegate object. The compiler uses the name of the event and adds the suffix "Event" to generate the name of the private field. This means that creating an event named largewithdraw will lead to the creation of a private field named largewithdrawevent.
The compiler also generates two methods to help register and deregister the delegate object to be used as an event handler. Both methods are named using standard naming conventions. The method used to register the event handler is named by adding the prefix "Add _" before the event name. The method used to log out of the event handler is named by adding the prefix "Remove _" before the event name. Therefore, the two methods created for the largewithdraw event are add_largewithdraw and remove_largewithdraw.
The Visual Basic. Net compiler generates an implementation for add_largewithdraw, which accepts the delegate object as a parameter and adds the delegate object to the handler list by calling the combine method of the delegate class. The compiler also generates an implementation for remove_largewithdraw, which deletes a handler method from the list by calling the Remove Method in the delegate class.
The fourth and last member added to the class definition is the member representing the event itself. In Figure 2, you should be able to find the event member named largewithdraw. This member has an inverted triangle next to it. However, you should note that the event member is not as physical as the other three members. Instead, it is a member that only contains metadata.
This event member that only contains metadata is very valuable because it can notify the compiler and other development tools that this class supports the standard mode for event registration in. NET Framework. The event member also contains the name of the registration method and logout method. This allows the compiler of Visual Basic. net, C #, and other hosting languages to find the name of the registration method during compilation.
Visual Studio. NET is another good example of finding a development tool for this event member that only contains metadata. When Visual Studio. NET discovers that a class definition contains an event, it automatically generates the main definition of the handler method and the code that registers them as the event handler.
Before discussing how to trigger an event, I would like to propose a limit related to the creation of the delegate type to be used to define the event. The delegate type used to define an event cannot return values. You must use the sub keyword instead of the function keyword to define the delegate type, as shown below:
'*** Can be used for events
Delegate sub baggagehandler ()
Delegate sub mailhandler (Itemid as integer)
'*** Cannot be used for events
Delegate function quoteofthedayhandler (funny as Boolean) as string
There are many reasons for this restriction. It is quite difficult to process the return value when it involves a multi-channel broadcast delegate bound to several handler methods. Calling invoke on a multicast delegate returns the same value as the last handler method in the call list. However, it is not that simple to capture the returned values of the handler methods that appeared previously in the list. Eliminating the need to capture multiple return values only makes the event easier to use.
Events
Now, let's modify the bankaccount class so that it can trigger an event when the withdrawal amount exceeds the $5000 threshold. The simplest way to trigger a largewithdraw event is to use the raiseevent keyword in the implementation of a method, attribute, or constructor. You may feel familiar with this syntax because it is similar to the syntax you used in earlier versions of Visual Basic. The following is an example of a largewithdraw event triggered by the withdraw method:
Class bankaccount
Public event largewithdraw as largewithdrawhandler
Sub withdraw (byval amount as decimal)
'*** Send notifications if required
If (amount> 5000) then
Raiseevent largewithdraw (amount)
End if
'*** Perform withdrawal
End sub
End Class
Although the syntax is the same as that in earlier versions of Visual Basic, what happens when an event is triggered is completely different. When you use the raiseevent keyword to raise an event, the Visual Basic. Net compiler generates the Code required to execute each event handler. For example, what do you think will happen when compiling the following code?
Raiseevent largewithdraw (amount)
The Visual Basic. Net compiler extends the expression to call invoke code on private fields that retain multicast delegate objects. In other words, using the raiseevent keyword has the same effect as writing the following code snippets:
If (not largewithdrawevent is nothing) then
Largewithdrawevent. Invoke (amount)
End if
Note that the code generated by the Visual Basic. Net compiler performs a check to ensure that the largewithdrawevent field contains a valid reference to an object. This is because the value of the largewithdrawevent field is always nothing before the first handler method is registered. Therefore, unless at present at least one handler method has been registered, the generated code will not attempt to call invoke.
You should be able to observe the event. You can use the raiseevent keyword or the largewithdrawevent private Field automatically generated by the compiler for direct programming. There is usually no difference. The two methods generate the same code:
'*** This code
Raiseevent largewithdraw (amount)
'*** Is the same as this code
If (not largewithdrawevent is nothing) then
Largewithdrawevent. Invoke (amount)
End if
In many cases, you may like to use the syntax of the raiseevent keyword because it requires less content to be typed and the generated code is more concise. However, explicit programming for the private fields of largewithdrawevent may make sense when more control is required. Let's look at an example of this situation.
Imagine the following situation: the bankaccount object has three Event Handlers registered to receive notifications of largewithdraw events. If you use the raiseevent keyword to trigger this event and the second event handler in the call list raises an exception, what will happen? The code line containing the raiseevent statement will receive a running exception, but you may not be able to determine which event handler causes the exception. In addition, exceptions caused by the second event handler may not be handled, and the third event handler cannot be executed as expected.
However, if you are willing to program based on the largewithdrawevent private field, you can handle exceptions caused by event handlers in a more appropriate way. View the code in figure 3. As you can see, downgrading to a lower level and programming based on this private delegate field provides additional control levels. You can properly handle the exception and continue executing the event handler that appears in the list. This method has obvious advantages over raiseevent syntax. In raiseevent syntax, exceptions caused by an event handler will prevent execution of any event handlers that subsequently appear in the call list.
Figure 3 using the private delegate Field
Sub withdraw (byval amount as decimal)
'*** Send notifications if required
If (amount> 5000) andaslo (not largewithdrawevent is nothing) then
Dim handler as largewithdrawhandler
For each handler in largewithdrawevent. getinvocationlist ()
Try
Handler. Invoke (amount)
Catch ex as exception
'*** Deal with exceptions as they occur
End try
Next
End if
'*** Perform withdrawal
End sub
Create and register an event handler
Now you know how to define and trigger events. Next we will discuss how to create an event handler and register it in a given source. There are two different methods in Visual Basic. Net to complete the above operations. The first method is dynamic event binding, which involves the use of addhandler keywords. The second method is static event binding. It involves the use of the familiar visual basic keyword withevents. I plan to discuss static event binding in future columns. So now let's take a look at how dynamic event binding works.
Remember that the event handler is a delegate object. Therefore, you can create an event handler by instantiating a delegate object from the delegate type on which the event is based. When creating the delegate object, you must bind it to the target handler method to be used as the event handler.
After creating an event handler, you must call a specific registration method on the event source to register it in a specific event. Recall that the registration method for the largewithdraw event is add_largewithdraw. When you call the add_largewithdraw method and pass the delegate object as a parameter, the event source adds the delegate object to the list of Event Handlers to receive Event Notifications.
Obfuscation of event registration is that you never directly call registration methods such as add_largewithdraw. In fact, if you try to access the event registration method by name, the Visual Basic. Net compiler will cause a compile-time error. However, you can use an alternative syntax that contains the addhandler statement. When you use the addhandler statement, the Visual Basic. Net compiler generates code that calls the event registration method.
Let's look at an example of using dynamic event registration to bind several event handlers. Assume that you have written the following shared method set in the accounthandlers class:
Class accounthandlers
Shared sub logwithdraw (byval amount as decimal)
'*** Write withdrawal info to log file
End sub
Shared sub getapproval (byval amount as decimal)
'*** Block until manager approval
End sub
End Class
What do you do if you want to use these methods as the event handler for the largewithdraw event of the bankaccount class? Let's start from creating an event handler bound to the handler logwithdraw. First, you must create a delegate object to be used as an event handler:
Dim handler1 as largewithdrawhandler
Handler1 = addressof accounthandlers. logwithdraw
Then, you must use the addhandler statement to register the new delegate object in the event source. When you use the addhandler statement to register an event handler, you need to pass two parameters, as shown below:
Addhandler <event>, <delegate Object>
The first parameter required by addhandler is the expression used to evaluate the events of a class or object. The second parameter is a reference to the delegate object bound to the event handler. The following is an example of registering an event handler in the largewithdraw event of the bankaccount object using the addhandler statement:
'*** Create bank account object
Dim account1 as new bankaccount ()
'*** Create and register event handler
Dim handler1 as largewithdrawhandler
Handler1 = addressof accounthandlers. logwithdraw
Addhandler account1.largewithdraw, handler1
When you use the addhandler keyword to register the event handler for the largewithdraw event, the Visual Basic. Net compiler will extend this code to call the registration method add_largewithdraw. Once the code containing the addhandler statement is executed, your event handler is ready to receive notifications. Therefore, the logwithdraw method is executed whenever the bankaccount object triggers the largewithdraw event.
In the previous example, I used a long syntax to describe exactly what happened when you created and registered an event handler. However, after understanding the principle, you may want to use a more concise syntax to achieve the same goal, as shown below:
'*** Create bank account object
Dim account1 as new bankaccount ()
Because the addhandler statement expects the delegate object to be referenced as the second parameter, you can use the simplified syntax of the addressof operator, followed by the name of the target handler method. When the Visual Basic. Net compiler detects this situation, it generates additional code to create a delegate object to be used as an event handler.
The addhandler statement in Visual Basic. NET is supplemented by the removehandler statement. Removehandler requires the same parameters as addhandler, but it has the opposite effect. The remove_largewithdraw method provided by the event source is called to delete the target handler method from the list of registered handlers:
Now you know all the steps required to implement callback Design Using events. The code in figure 4 shows a complete application. In this application, two event handlers have been registered to receive callback notifications for largewithdraw events from the bankaccount object.
Figure 4 an event-based design for callback communications
Delegate sub largewithdrawhandler (byval amount as decimal)
Class bankaccount
Public event largewithdraw as largewithdrawhandler
Sub withdraw (byval amount as decimal)
'*** Send notifications if required
If (amount> 5000) then
Raiseevent largewithdraw (amount)
End if
'*** Perform withdrawal
End sub
End Class
Class accounthandlers
Shared sub logwithdraw (byval amount as decimal)
'*** Write withdrawal info to log file
End sub
Shared sub getapproval (byval amount as decimal)
'*** Block until manager approval
End sub
End Class
Module MyApp
Sub main ()
'*** Create bank account object
Dim account1 as new bankaccount ()
'*** Register event handlers
Addhandler account1.largewithdraw ,_
Addressof accounthandlers. logwithdraw
Addhandler account1.largewithdraw ,_
Addressof accounthandlers. getapproval
'*** Do something that triggers callback
Account1.withdraw (5001)
End sub
End Module
Summary
Although the motivation for using events and some syntaxes are the same as those in earlier versions of Visual Basic, you must acknowledge that the current situation is much different. As you can see, you have more control over how to respond to events than before. This is especially true if you want to lower the level and program based on delegation.
In the next issue of the "basic functions" column, I plan to continue with the discussion of related events. I will explain to you how Visual Basic. Net supports static event binding through the withevents keyword syntax you are familiar with, and will discuss the handles clause. To truly control events, you must be able to easily control dynamic event registration and static event registration.
Please send TED questions and comments to instinct@microsoft.com.
Ted Pattison is a lecturer and researcher at developmentor (http://www.develop.com) who manages Visual Basic courses with others at developmentor. He is the author of programming distributed applications with COM and Microsoft Visual Basic 6.0 (Microsoft Press, 2000.
The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion;
products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the
content of the page makes you feel confusing, please write us an email, we will handle the problem
within 5 days after receiving your email.
If you find any instances of plagiarism from the community, please send an email to:
info-contact@alibabacloud.com
and provide relevant evidence. A staff member will contact you within 5 working days.