Event does not use EventHandler(Of TEventArgs)
ID |
vbnet.maintainability.use_generic_event_handler_instances |
Severity |
low |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Api Design |
Language |
VB.NET |
Description
Reports an event that does not use the generic EventHandler(Of TEventArgs) delegate. Two forms
are reported: the inline-signature form Event Progress(sender As Object, e As MyEventArgs),
where the compiler generates an implicit delegate type for the event, and the explicit form
Event Completed As SomeHandler, where the named delegate is neither EventHandler nor
EventHandler(Of T). Events typed as EventHandler(Of T), and the data-less
Event Closed As EventHandler, are not reported.
Rationale
Every delegate declared for an event is a type that has to be maintained, and it buys nothing
that EventHandler(Of T) does not already provide. The cost shows up as soon as the event data
changes: adding a field to the arguments means editing the delegate as well as the arguments
class, and any change to the parameter list is a breaking change for every subscriber, whereas
the generic form keeps the signature fixed and lets the EventArgs subclass carry whatever the
event needs. The inline-signature form has an additional problem - the delegate it generates is
implicit, so there is no named type for a subscriber to declare a variable of, to store in a
collection of handlers, or to pass through a generic helper, and tooling that reflects over the
event surfaces a compiler-generated name. Custom delegates also drift apart: one takes
(sender, e), the next takes (attempt As Integer), and generic infrastructure such as a
logging or retry wrapper that wants to subscribe to "any event of this object" cannot be written
against them. Standardising on EventHandler(Of T) makes every event in the codebase the same
shape, which is what lets that kind of shared code exist at all.
The following code illustrates the pattern detected by this rule:
Public Class TransferAgent
' FLAGGED: Event does not use EventHandler(Of TEventArgs)
Public Event Progress(sender As Object, e As TransferEventArgs)
' FLAGGED: Event does not use EventHandler(Of TEventArgs)
Public Event Completed As TransferCompletedHandler
' FLAGGED: Event does not use EventHandler(Of TEventArgs)
Protected Event Retried(attempt As Integer)
Remediation
Declare the event as EventHandler(Of TEventArgs) and move the data into a class deriving from
EventArgs, then delete the custom delegate. Keep the conventional
(sender As Object, e As TEventArgs) shape so subscribers can identify the sender, and use the
non-generic EventHandler when the event carries no data at all.
' Before: an implicit delegate, and a custom one that must be maintained
Public Event Progress(sender As Object, e As TransferEventArgs)
Public Event Completed As TransferCompletedHandler
' After
Public Event Progress As EventHandler(Of TransferEventArgs)
Public Event Completed As EventHandler(Of TransferEventArgs)