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)

Configuration

This detector does not need any configuration.