Visual Basic Concepts
When to Use Events or Call-Backs for Notifications
The topics "Asynchronous Notifications Using Call-Back Methods" and "Asynchronous Notifications Using Events" demonstrate that call-backs are more work to implement than events. However, you should not base your decision about which to use on the amount of work involved. Call-backs and events represent different approaches to communication, and you should select the approach that best fits your needs.
You can characterize the difference between events and call-backs as follows: An event is like an anonymous broadcast, while a call-back is like a handshake.
The corollary of this is that a component that raises events knows nothing about its clients, while a component that makes call-backs knows a great deal.
Here’s what these things mean to you as a developer:
A client that has a reference to an object that raises events can handle those events by placing the reference into a WithEvents variable. The object that raises the events has no information about its clients. It's broadcasting to an unknown audience — perhaps to an empty theater.
By contrast, a component that's making call-backs must have a reference to every object it's going to call. It has to know exactly how many of them there are.
An object that raises events has no control over the order in which clients receive its events. (And you should take care to avoid creating dependencies on any ordering you may observe.)
By contrast, a component making call-backs can control the order in which it calls clients back. It might give some clients higher priority, for example.
When an object raises an event, all of its clients handle the event before the object that raised the event gets control again.
By contrast, a component making call-backs gets control back after each call it makes to a client.
If an event contains a ByRef argument, that argument can be altered by any client that handles the event. The last client's changes are the only ones visible to the object that raised the event, because (as noted above) the object that raised the event doesn't get control again until all of its clients have handled the event.
By contrast, a component making call-backs can examine changes to ByRef arguments after every call to a client, and can pass the next client fresh values for those arguments.
If an unhandled error occurs in a client's event handler, the object that raised the event cannot receive the error. If the object is provided by an in-process component running in the client’s address space, both client and component will terminate as a result of the unhandled error.
By contrast, a component making call-backs will receive errors that occur in the call-back method, and must be prepared to handle them.
*Note* An additional difference between events and call-back methods is that events cannot have optional arguments, named arguments, or ParamArray arguments.
A component can provide some notifications using call-backs, and some using events. The nature of the notification determines which mechanism should be used. You should use an event to provide a notification when all of the following statements are true:
The notification can be broadcast anonymously.
The order in which clients receive the notification is not important.
There’s no need for the component to get control back until all clients have received the notification.
If the notification involves ByRef arguments, there’s no need for the component to test the values of those arguments after each client receives the notification. The component only needs to know the last value assigned. (You can plan for clients to cooperate in their use of a ByRef argument — for example, once a Cancel argument is set to True, no client changes it — but there’s no way to enforce this.)
The component doesn't need to know about errors that occur in the client.
If any one of the statements above is false, then you should do the extra work necessary to provide the notification using call-back methods.
You may also want to do the extra work to use call-backs when performance is critical. You can get vtable binding with call-back methods by using the Implements statement to add the call-back interface to the client's call-back object. Events are not vtable bound. (This will be much more noticeable with an in-process component that provides events or call-backs.)