Avoid Unsealed Attributes

ID

csharp.avoid_unsealed_attributes

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Efficiency

Language

CSharp

Tags

attributes, efficiency, performance

Description

Reports custom Attribute classes declared without the sealed modifier. The CLR’s attribute-reflection paths are significantly faster when the attribute type is sealed because they can skip the inheritance walk.

Rationale

The .NET design guidelines explicitly recommend sealing attribute classes. Every attribute in the base class library is sealed (SerializableAttribute, ObsoleteAttribute, FlagsAttribute, …​). Leaving an attribute open for inheritance pays a reflection cost on every lookup and almost never reflects an intentional extensibility point.

public class MyAttribute : Attribute { }              // FLAW

public sealed class MyOtherAttribute : Attribute { }  // OK
The check is conservative about what counts as an attribute class. A class derives from Attribute when its base is Attribute/System.Attribute, or when a base class declared in the same file transitively reaches it. A base class name that merely ends with Attribute but is declared elsewhere (or not at all in this file) is not enough on its own — Attribute is also a common domain term (e.g. a product or customer attribute entity such as Color or Size), so an unresolved base is left unreported rather than guessed at:
public abstract class BaseAttribute : BaseEntity { }  // domain entity, not System.Attribute

public class AddressAttribute : BaseAttribute { }     // OK, base chain doesn't reach Attribute

Remediation

Add the sealed modifier to the class header. If the attribute is genuinely intended as a base for further attributes, mark it abstract instead and document the extensibility point.