Inheritance is one of the pillar of OOP. However, in the real world, most classes are not designed to be properly inheritable.

Properly designing a class to be inheritable is a tricky task. One must anticipate state initialization, state correctness, state mutability and also non-public methods calls from descendants. Anticipating well code re-use is hard and this requires experience. As a consequence there is no chance that a class gets well designed for inheritance by chance.

However, in both C# and VB.NET, a class is by default inheritable. The C# keyword sealed and the VB.NET keyword NotInheritable must be specified explicitly. Since there is no compiler warning most developers don’t bother to seal their classes. Hence most classes are not properly designed for inheritance but are implicitly declared as inheritable.

As if writing high quality code was not complicated enough here we have a language pitfall.

This is why NDepend proposes the default rule Class with no descendant should be sealed if possible. The CQLinq source code of this rule is pretty obvious:

Rule: Class with no descendant should be sealed if possible // <Name>Class with no descendant should be sealed if possible</Name> // <Id>ND1203:ClassWithNoDescendantShouldBeSealedIfPossible</Id> warnif count > 0 from t in JustMyCode.Types where t.IsClass && t.NbChildren ==0 && !t.IsSealed && !t.IsStatic && !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. orderby t.NbLinesOfCode descending select new { t, t.NbLinesOfCode, Debt = 30.ToSeconds().ToDebt(), Severity = Severity.Medium } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // <Name>Class with no descendant should be sealed if possible</Name> // <Id>ND1203:ClassWithNoDescendantShouldBeSealedIfPossible</Id> warnif count > 0 from t in JustMyCode . Types where t . IsClass && t . NbChildren == 0 && ! t . IsSealed && ! t . IsStatic && ! 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. orderby t . NbLinesOfCode descending select new { t , t . NbLinesOfCode , Debt = 30.ToSeconds ( ) . ToDebt ( ) , Severity = Severity . Medium }

As explained in the rule’s comments, you won’t get warnings per default for public non-sealed classes with no descendants. Indeed, it might be well that the analyzed code base is a framework and that descendants of such classes are not available at the time of analysis. But if the code base analyzed is an application with no public API it is important to comment this clause and get most public classes declared as sealed.

If you are not convinced that most classes should be sealed, there are also non-obvious reasons. Here is a quote from Jeffrey Richter from his essential book CLR via C#:

When defining a new type, compilers should make the class sealed by default so that the class cannot be used as a base class. Instead, many compilers, including C#, default to unsealed classes and allow the programmer to explicitly mark a class as sealed by using the sealed keyword. Obviously, it is too late now, but I think that today’s compilers have chosen the wrong default and it would be nice if it could change with future compilers. There are three reasons why a sealed class is better than an unsealed class: Versioning : When a class is originally sealed, it can change to unsealed in the future without breaking compatibility. (…)

: When a class is originally sealed, it can change to unsealed in the future without breaking compatibility. (…) Performance : (…) if the JIT compiler sees a call to a virtual method using a sealed types, the JIT compiler can produce more efficient code by calling the method non-virtually.(…)

: (…) if the JIT compiler sees a call to a virtual method using a sealed types, the JIT compiler can produce more efficient code by calling the method non-virtually.(…) Security and Predictability: A class must protect its own state and not allow itself to ever become corrupted. When a class is unsealed, a derived class can access and manipulate the base class’s state if any data fields or methods that internally manipulate fields are accessible and not private.(…)

In the same vein you can also refer to this Eric Lippert 2004 post where he explains why most .NET Framework classes are sealed per default.