109

I'd like to hear what is the motivation behind the bulk of sealed classes in the .Net framework. What is the benefit of sealing a class? I cannot fathom how not allowing inheritance can be useful and most likely not the only one fighting these classes.

So, why is the framework designed this way and wouldn't it be unbreaking change to unseal everything? There must be another reason than just being evil?

David Klempfner
  • 8,700
  • 20
  • 73
  • 153
mmiika
  • 9,970
  • 5
  • 28
  • 34
  • 2
    I'd say [don't *ever* seal a class unless you *know* you'll have support issues with your clients.](http://programmers.stackexchange.com/a/210481/4261) – cregox Sep 04 '13 at 19:40
  • 1
    Possible duplicate of [Why there is an option for a class in OOP be marked to never inherit from ancestral?](http://stackoverflow.com/questions/12588158/why-there-is-an-option-for-a-class-in-oop-be-marked-to-never-inherit-from-ancest/21116956) – Doval Jan 27 '14 at 17:51

10 Answers10

113

Classes should either be designed for inheritance or prohibit it. There is a cost to designing for inheritance:

  • It can pin down your implementation (you have to declare which methods are going to call which other methods, in case a user overrides one but not the other)
  • It reveals your implementation rather than just the effects
  • It means you have to think of more possibilities when designing
  • Things like Equals are hard to design in an inheritance tree
  • It requires more documentation
  • An immutable type which is subclassed may become mutable (ick)

Item 17 of Effective Java goes into more details on this - regardless of the fact that it's written in the context of Java, the advice applies to .NET as well.

Personally I wish classes were sealed by default in .NET.

Mitch Wheat
  • 295,962
  • 43
  • 465
  • 541
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 31
    Hmm.. if you extend a class, isn't it your problem if you break it? – mmiika Nov 06 '08 at 11:04
  • 28
    What if an implementation change you have no control over in the base class breaks you? Whose fault is that? Inheritance introduces fragility, basically. Favouring composition over inheritance promotes robustness, IMO. – Jon Skeet Nov 06 '08 at 11:05
  • 2
    True. Ok, makes sense if you provide some means (separate interface perhaps) to write your own implementation for testability purposes. I've just spent one too many hour writing decorators around these classes :) – mmiika Nov 06 '08 at 11:10
  • 7
    Yes, interfaces are nice - and yes, you can favour composition anyway. But if I expose an unsealed base class without thinking very carefully about it, I should expect that changes might well break derived classes. That feels like a bad thing to me. Better to seal the class and avoid breakage, IMO. – Jon Skeet Nov 06 '08 at 11:17
  • Hey Jon, your reply is very good. I am wondering if you can tell me what composition is, and where can I find more info about it? I always thought OO is inheritance, not that I used inheritance all over. – Joan Venge May 26 '09 at 20:20
  • 9
    @Joan: Composition is a "has-a" relationship rather than "is-a". So if you want to write a class which can act like a list in some ways, but not in others, you might want to create a class with a List member variable, rather than deriving from List. You'd then *use* the list to implement various methods. – Jon Skeet May 26 '09 at 20:23
  • Can compiler extension methods essentially circumvent the class' original author's attempt to seal his class and therefore reintroduce the possibility of "breaking" the class in the same way that you suggested that allowing inheritance on the class could also break those that use the class? For example, what if someone where to add an extension method that added another property to the object that should have resulted in the IsEqual method being modified to consider the new property's value? I'm a little out of my league here.... – Chad Jan 24 '13 at 04:47
  • Hmm. In re-reading my question back after posting, it occurred to me that an extension method cannot be a property, so maybe that was a back example but I am still wondering if the jist of the question remains. – Chad Jan 24 '13 at 04:49
  • 1
    @ChadD: Extension methods never change how a class is treated polymorphically, and are only searched for *after* a member lookup fails. So it can't really break the class in this way, no. – Jon Skeet Jan 24 '13 at 06:41
  • 1
    The notion of a class being "designed for inheritance" is weird to me. Inheriting from a class is the *derived class' responsibility* not the base class'. What the base class has to do is not *break parameter calls* in new versions. Radically changing a class between versions would break existing code, anyway. Inheritance has a purpose. Sealed is just defeating this purpose. It is funny people went into such effort to create OOP, to see a company wanting to shoot it down like that. Well, funny in a tragic sense of the word. – ThunderGr Oct 21 '13 at 08:18
  • 3
    @ThunderGr: Which company are you talking about? And no, a base class would have to do more than that. If any one virtual method calls another virtual method, that would then need to be documented and not change - otherwise subclasses could be broken by that sort of change. When a class is designed with inheritance in mind, it limits the freedom to change the implementation later on. And of course inheritance can be useful - at times. At other times it can add complexity with no benefit, where composition would simplify things. It's all a matter of context. – Jon Skeet Oct 21 '13 at 08:26
  • 3
    Myself, I rarely use inheritance. Usually, I prefer composition. However, inheritance is a very important feature of OOP. The *implementation* of the base class should be irrelevant to a derived class. The Interface of the base class should not change in such a way that would invalidate protected or public members. If a class is not fit for inheritance, it is a badly designed class, to begin with. Any change of a class that would not break existing code should not break derived classes, either. – ThunderGr Oct 21 '13 at 09:05
  • 3
    @ThunderGr: But that's my point: when you don't need to worry about what other implementations are provided by subclasses, you can be freer with your own implementation. For example, suppose one method is implemented by calling another method, and both are virtual. That needs to be documented - even though it *feels* like an implementation detail. Basically, designing for inheritance adds handcuffs - which are appropriate in some cases, but not in others. I'd rather have a sealed class implementing an interface than an unsealed class with a bunch of virtual methods. – Jon Skeet Oct 21 '13 at 11:04
  • 1
    If you implement another virtual method, that calls a method also defined as virtual, existing derived classes will not break. New derived classes may benefit from knowing so but *it is not critical* if they do. Overriding either of your methods should be the same to them, if they do not need to override both. The runtime should properly call the base.virtual for the called virtual function not implemented in the derived class. I understand your personal preference, of course, but limiting expansion is not the solution, IMHO. – ThunderGr Oct 21 '13 at 11:19
  • 3
    @ThunderGr Sorry, but the sub classes CAN break, even for changes that preserve the superclass's behavior. Proof would require far more space than the comments section would provide, but there have been at least [two](http://www.cas.mcmaster.ca/~emil/Publications_files/MikhajlovSekerinski98FragileBaseClassProblem.pdf) [papers](http://www.cs.cmu.edu/~aldrich/papers/selective-open-recursion.pdf) that cover the topic in great detail. Avoiding this problem requires deliberate design decisions to forbid overriding certain methods (see second paper). – Doval Jan 27 '14 at 17:49
  • @Doval Great links. Thank you for providing them. I never denied the fact that subclasses *can* break if inheritance is misused and implementation details of the base class become significant to the child class. I only pointed out that, by doing so, the designer of the child class is responsible for any breaking of his class, due to his poor design decisions. The solution proposed by the paper is quite interesting. I would like to see it implemented. On a second note, when someone designing a base class decides to call a *public method* from *another public method*, surely invites problem. – ThunderGr Jan 28 '14 at 07:40
  • 2
    @ThunderGr The problem is that most people aren't even aware this is a problem; it's not at all obvious that without special precautions you can break subclasses. Most introductory texts for OOP mention inheritance like it's this great and clever thing - like you're expected to make heavy use of it as you advance as a programmer. That aside, it's not entirely clear to me which situation actually requires inheritance; almost all of its uses are hacks to get around not having first-class functions or sum types. – Doval Jan 30 '14 at 23:00
  • @JonSkeet Designing a class on a day to day thinking about inheritance and all usage permutations is something I'm far from mastering. Open and closed principle is something very difficult to achieve and requires a very experienced programmer to master. When I first heard about it it all made sense but I find myself often having to readjust classes based on new requirements. My question is, when you don't think too much about inheritance when designing a class do you by default seal it? Or every class you write has a very thoughtful inheritance-aware design behind? – johnildergleidisson Mar 11 '16 at 16:12
  • @JoaoMilasch: I seal by default, basically - unless I'm designing something specifically for inheritance, it makes sense to seal it IMO, and I rarely need to design for inheritance. – Jon Skeet Mar 11 '16 at 16:14
  • Is there any practical difference between a sealed class and an unsealed class without any virtual members, other than being explicit about disallowing inheritance? I rarely use inheritance, so maybe I'm forgetting something obvious here. – user247702 Aug 10 '17 at 14:08
  • 1
    @Stijn: Absolutely. Suppose I write a class (`Foo`) which is immutable, but don't seal it. Anyone can create a mutable subclass, which means that if I'm working with a reference of type `Foo`, I don't know whether it's safe to share it between threads etc. That's just one example - it's all about how much you can be sure about. – Jon Skeet Aug 10 '17 at 14:13
  • That would only be possible when `Foo` has `protected` or `internal` members, correct? My classes only have `public` or `private` members, unless I specifically design for inheritance to begin with. But I do agree with the idea of sealing by default. (I don't mind posting a new question if you think this derives too much from the original question.) – user247702 Aug 10 '17 at 14:23
  • 2
    @Stijn: No, it would be possible if the subclass added its own state. Just because it's not observably mutable as `Foo` doesn't mean it's still immutable. (Someone else may end up casting to the subclass.) – Jon Skeet Aug 10 '17 at 14:28
42
  • Sometimes classes are too precious and not designed to be inherited.
  • Runtime/Reflection can make inheritance assumptions about sealed classes when looking for types. A great example of this is - Attributes are recommended to be sealed for lookup runtime speed. type.GetCustomAttributes(typeof(MyAttribute)) will perform significantly faster if MyAttribute is sealed.

The MSDN article for this topic is Limiting Extensibility by Sealing Classes.

CVertex
  • 17,997
  • 28
  • 94
  • 124
  • 3
    Glad to see they clearly say "use with caution" now... wish they would practice what they preach though. – mmiika Nov 06 '08 at 10:54
  • 14
    That looks like bad advice to me :( – Jon Skeet Nov 06 '08 at 11:01
  • I don't have an opinion, it's not my advice. C# gives you control - which is okay. I've never ran into an issue with sealed classes (i.e. having the need to derive from one). If I did, I'd just wrap the sealed class instead. – CVertex Nov 06 '08 at 11:10
  • 4
    @CVertex: Sorry, I wasn't trying to criticize you - just the article. – Jon Skeet Nov 06 '08 at 11:15
  • @Jon- can you elaborate on why that article contains bad advice? – a developer Jul 21 '11 at 19:24
  • 17
    @generalt: I believe in designing for inheritance or prohibiting it. Designing for inheritance takes quite a bit of work and will often limit implementations in the future. Inheritance also introduces uncertainty into callers as to exactly what they'll be calling into. It also doesn't mix well with immutability (which I'm a fan of). I only find class inheritance useful in a relatively small number of places (whereas I love interfaces). – Jon Skeet Jul 21 '11 at 19:46
  • 1
    @CVertex If you've used .NET you've likely run into the issue and just not noticed, nearly all .NET core classes are sealed. – CoryG Dec 24 '16 at 23:18
  • 1
    @CoryG [Ohad Schneider](https://stackoverflow.com/users/67824/ohad-schneider) indicates otherwise in [his answer](https://stackoverflow.com/a/44872265/1497596): *"Indeed, if you [search the ASP.Net Core codebase](https://github.com/aspnet/Mvc/search?utf8=%E2%9C%93&q=%22sealed%20class%22&type=), you will only find about 30 occurrences of `sealed class`, most of which are attributes and test classes."* – DavidRR Dec 01 '17 at 16:13
  • 1
    @DavidRR try inheriting from .NET core classes. Unless they've changed the entire architecture in the last year and I just haven't noticed since you'll have some serious issues with about 70% of it, mostly helper classes. – CoryG Dec 01 '17 at 17:59
16

It seems that the official Microsoft guidelines on sealing have evolved since this question was asked ~9 years ago, and they moved from an opt-in philosophy (seal by default) to opt-out (don't seal by default):

X DO NOT seal classes without having a good reason to do so.

Sealing a class because you cannot think of an extensibility scenario is not a good reason. Framework users like to inherit from classes for various nonobvious reasons, like adding convenience members. See Unsealed Classes for examples of nonobvious reasons users want to inherit from a type.

Good reasons for sealing a class include the following:

  • The class is a static class. See Static Class Design.
  • The class stores security-sensitive secrets in inherited protected members.
  • The class inherits many virtual members and the cost of sealing them individually would outweigh the benefits of leaving the class unsealed.
  • The class is an attribute that requires very fast runtime look-up. Sealed attributes have slightly higher performance levels than unsealed ones. See Attributes.

X DO NOT declare protected or virtual members on sealed types.

By definition, sealed types cannot be inherited from. This means that protected members on sealed types cannot be called, and virtual methods on sealed types cannot be overridden.

✓ CONSIDER sealing members that you override. Problems that can result from introducing virtual members (discussed in Virtual Members) apply to overrides as well, although to a slightly lesser degree. Sealing an override shields you from these problems starting from that point in the inheritance hierarchy.

Indeed, if you search the ASP.Net Core codebase, you will only find about 30 occurences of sealed class, most of which are attributes and test classes.

I do think that immutability conservation is a good argument in favor of sealing.

Ohad Schneider
  • 36,600
  • 15
  • 168
  • 198
4

I found this sentence in msdn documentation: "Sealed classes are primarily used to prevent derivation. Because they can never be used as a base class, some run-time optimizations can make calling sealed class members slightly faster."

I don't know if the performance is the only advantage of sealed classes and personally I also would like to know any other reasons ...

bruno conde
  • 47,767
  • 15
  • 98
  • 117
3

Performance is an important factor for example, the string class in java is final(<- sealed) and reason for this is performance only. I think another important point is to avoid the brittle base class problem described in detail here: http://blogs.msdn.com/ericlippert/archive/2004/01/07/virtual-methods-and-brittle-base-classes.aspx

If you provide a framework it is important for maintainability legacy projects and to upgrade your framework to avoid the brittle base class problem

Peter Parker
  • 29,093
  • 5
  • 52
  • 80
  • The reason for String in java being final is not performance, it's security. – CesarB Nov 06 '08 at 17:21
  • @CesarB: Yes, but also, String is not a normal Java class. It is the only (I believe) class in Java that supports operator overloading (for more, see [here](http://stackoverflow.com/a/194889), section: "Even C and Java have (hardcoded) operator overloading"), which is not possible in a normal class. Because of this, the `String` class might not even be possible to subclass, even if it weren't final. – wchargin Jan 07 '12 at 17:45
1

Sealed is used to prevent the "brittle base class problem". I found a good article in MSDN that explains that.

ihebiheb
  • 3,673
  • 3
  • 46
  • 55
0

Sealing allows you to realize some minor performance gains. This is less true in the world of JITs and lazy pessimization than in the world of, say C++, but since .NET is not as good as pessimization as java compilers are mostly because of different design philosophies it is still useful. It tells the compiler that it can directly call any virtual methods rather than call them indirectly through the vtable.

It is also important when you want a 'closed world' for things like equality comparison. Normally once I define a virtual method, I'm pretty much hosed for defining a notion of equality comparison that really implements the idea. On the other hand, I might be able to define it for a particular subclass of the class with the virtual method. Sealing that class ensures that equality really does hold.

Edward Kmett
  • 29,632
  • 7
  • 85
  • 107
0

Sealing a class makes managing disposable resources easier.

Jeff Dunlop
  • 893
  • 1
  • 7
  • 20
0

To determine whether to seal a class, method, or property, you should generally consider the following two points:

•The potential benefits that deriving classes might gain through the ability to customize your class.

•The potential that deriving classes could modify your classes in such a way that they would no longer work correctly or as expected.

0

A further consideration is that sealed classes can't be stubbed in your unit tests. From Microsoft's documentation:

Sealed classes or static methods can't be stubbed because stub types rely on virtual method dispatch. For such cases, use shim types as described in Using shims to isolate your application from other assemblies for unit testing

Dave Clark
  • 2,243
  • 15
  • 32