Concrete custom attribute class is not declared NotInheritable
ID |
vbnet.maintainability.avoid_unsealed_attributes |
Severity |
low |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Api Design |
Language |
VB.NET |
Description
Reports a class that inherits from a type whose name ends in Attribute - Attribute itself,
System.Attribute, or an existing attribute such as ValidationAttribute - and is declared
neither NotInheritable nor MustInherit. A type explicitly designed as a base for other
attributes is expected to say so with MustInherit, and is not reported.
Rationale
An attribute is metadata, not a class hierarchy, and leaving one open creates a distinction the
consuming code has to be aware of but almost never is. A derived attribute is a different type:
GetCustomAttribute(Of LoggedAttribute) finds a VerboseLoggedAttribute because it is
assignable, while a lookup or a Select Case written against the exact type does not - so the
same annotation is honoured on one code path and ignored on another, depending on how the
author of each path happened to ask. It also commits the type to being subclassable: once
someone derives from it, the constructors and the property set are a public contract that
cannot be tightened, and adding a required constructor parameter breaks code outside the
project. NotInheritable costs one keyword, is the shape almost every attribute wants, and
removes both questions.
The following code illustrates the pattern detected by this rule:
Namespace Acme.Diagnostics
' FLAGGED: Concrete custom attribute class is not declared NotInheritable
Public Class LoggedAttribute
Inherits Attribute
Public Property Channel As String
End Class
Remediation
Add NotInheritable to the class declaration. If the type really is meant to be a base for a
family of attributes, declare it MustInherit instead and make the concrete leaves
NotInheritable, so the intent is explicit in either case. Where behaviour genuinely needs to
vary, prefer a property on one NotInheritable attribute over a family of derived attributes.
' Before: open to subclassing, so lookups by exact type behave inconsistently
Public Class LoggedAttribute
Inherits Attribute
' After
Public NotInheritable Class LoggedAttribute
Inherits Attribute