Class with no descendant should be sealed if possible
A non-static class not declared sealed can be subclassed wherever visible. Being a base class requires significant design effort; subclassing a class not designed for it leads to unanticipated issues. Most classes are non-sealed by neglect, not intention.
sealed brings performance gains (benchmark: https://www.meziantou.net/performance-benefits-of-sealed-class.htm) but its real benefit is to express the intention: not designed to be a base class, subclassing not allowed.
Public classes are not matched by default (they may be subclassed by library consumers); for an application, uncomment !t.IsPubliclyVisible.
sealed brings performance gains (benchmark: https://www.meziantou.net/performance-benefits-of-sealed-class.htm) but its real benefit is to express the intention: not designed to be a base class, subclassing not allowed.
Public classes are not matched by default (they may be subclassed by library consumers); for an application, uncomment !t.IsPubliclyVisible.
How to fix
Assess whether each matched class is really meant to be subclassed; most will end up declared sealed.
CQLinq source
This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.
// <Name>Class with no descendant should be sealed if possible</Name>
// <Id>ND1203:ClassWithNoDescendantShouldBeSealedIfPossible</Id>
warnif count > 0
let entryPoints = Application.Methods.Where(m => m.IsEntryPoint)
.ParentTypes().Distinct().ToHashSetEx()
from t in JustMyCode.Types where
t.IsClass &&
t.NbChildren ==0 &&
!t.IsSealed &&
!t.IsStatic &&
!t.IsAbstract &&
!t.IsGeneratedByCompiler && // Top Level Statement Program class is not sealed
!t.IsPubliclyVisible // You might want to comment this condition
// if you are developing an application,
// instead of developing a library
// with public classes that are intended to be
// sub-classed by your clients.
&& !entryPoints.Contains(t) // Don't match un-editable Program types
orderby t.NbLinesOfCode descending
select new {
t,
t.NbLinesOfCode,
Debt = 30.ToSeconds().ToDebt(),
Severity = Severity.Medium
}
//<Expl>No class derives from the *{0}* and it is not publicly visible. It can be marked as *sealed*.</Expl>
//<Desc>
// A *non-static* class not declared *sealed* can be subclassed wherever
// visible. Being a *base class* requires significant design effort;
// subclassing a class not designed for it leads to unanticipated issues.
// Most classes are *non-sealed* by neglect, not intention.
//
// *sealed* brings performance gains (benchmark:
// https://www.meziantou.net/performance-benefits-of-sealed-class.htm)
// but its real benefit is to **express the intention**: *not designed to be
// a base class, subclassing not allowed*.
//
// *Public* classes are not matched by default (they may be subclassed by
// library consumers); for an application, uncomment *!t.IsPubliclyVisible*.
//</Desc>
//<HowToFix>
// Assess whether each matched class is really meant to be subclassed; most
// will end up declared *sealed*.
//</HowToFix>
Open in the interactive Rules Explorer · All default rules
Object Oriented Design: all 14 items
- Avoid interfaces too big ND1200
- Base class should not use derivatives ND1201
- Class shouldn't be too deep in inheritance tree ND1202
- Class with no descendant should be sealed if possible ND1203
- Overrides of Method() should call base.Method() ND1204
- Do not hide base class methods ND1205
- A stateless class or structure might be turned into a static type ND1206
- Non-static classes should be instantiated or turned to static ND1207
- Methods should be declared static if possible ND1208
- Constructor should not call a virtual method ND1209
- Avoid the Singleton pattern ND1210
- Don't assign static fields from instance methods ND1211
- Avoid empty interfaces ND1212
- Avoid types initialization cycles ND1213