9

Reading this article (by Eric Lippert), it has four arguments as to why you should use sealed, however, I don't understand why we actually need it. Philosophical/aesthetic reasons aside, why do we need to use sealed?

I can see how things like readonly from C# or const from C++ actually catch genuine errors. Is there any type of error that sealed can catch?

An often cited reason for sealing a class is because, it hasn't been to designed to be extended. What does that mean? Why is it important? Are there any real examples of this?

The point on compatibility isn't a reason why we need to seal classes but why it should be default.

The final point again is strange, because sealing your class doesn't prevent someone providing an alternative implementation in a heirachy.


If I didn't put enough emphasis, on it already I'm looking for reasons we actually need it. Not the usual, "someone else might write bad code". I understand that plenty of people like using sealed for design intent, but I'm looking for a practical purpose beyond that.

5
  • The explicit purpose is to prevent a class from being inherited. If I make a class Car that inherits from class Giraffe I'm saying that cars are giraffes and that may have implications the writer of Giraffe never coded for resulting in system instability. I'm not saying someone can't offer an alternative implementation, they just can't paste it over an existing one that wasn't designed to account for that.
    – Kevin
    Commented Jan 9, 2014 at 21:19
  • 1
    Related How can designing for inheritance cause extra cost
    – user40980
    Commented Jan 9, 2014 at 21:20
  • 2
    Are you able to write a C# program without using it? If so, you don't 'need' to use it. Commented Jan 9, 2014 at 22:50
  • 2
    Eric thinks from the point of view of a library developer. If a change to a library breaks a consumer, the library author can't make that change anymore. So it's useful to restrict a consumer from using it in a way that you're not willing to support. Restricting the consumer increases the flexibility of the library developer. Commented Jan 10, 2014 at 12:46
  • 2
    Not every language feature is something that you need. Sometimes, they're just nice to have, offering a syntactic or semantic alternative to a solution that might be more cumbersome.
    – KChaloux
    Commented Jan 10, 2014 at 14:11

5 Answers 5

5

What does that mean?

It means that it is difficult for people to obey the Liskov Substitution Principle (or less likely, the Open Closed Principle) with the class as is. It has subtle, or difficult to enforce behavior that inheritors are likely to screw up. Or they're not likely to screw up, but the impact of screwing them up is huge.

The final point again is strange, because sealing your class doesn't prevent someone providing an alternative implementation.

Sure it does. If some method takes object A or some other class uses type A, by having A sealed, people cannot override it with B and then pass B into the method or use it in the class. Consumers of A can see it's sealed and know that A is the only behavior they're getting.

All that said, we don't need sealed - just like we don't need readonly. They are tools to help programmers prevent misuse, because misuse leads to bugs. Though personally, I dislike sealed; even in these days where composition over inheritence is well known, it is rare that you can say "nobody will ever need to extend this" and actually be right.

4
  • So you're saying sealed prevents LSP violations? Commented Jan 9, 2014 at 21:08
  • 7
    @DaveHillier - If people can't inherit from your class, they can't make a subtype that violates LSP.
    – Telastyn
    Commented Jan 9, 2014 at 21:11
  • This answer you gave is related and at least a partial answer: programmers.stackexchange.com/a/210474/37972 Commented Jan 9, 2014 at 21:30
  • 1
    It's a big sign "here be dragons". You can remove the sign, but that doesn't make the dragons go away.
    – gnasher729
    Commented Aug 12, 2022 at 9:30
2

Reading around I've found a few things, a bit less abstract than LSP violations (although they could be considered to be):

  • An immutable type which is subclassed may become mutable (bad for multi-threading, etc)
  • Polymorphic equality, you can then guarantee that the types are the same and not just equivalent.

Sealing classes also offers some Runtime/Reflection performance improvements

0

Consider these facts:

  • When classes are not sealed, you can inherit from them. In doing so, you can change how this class behaves.
  • Polymorphism allows you to pass a derived type into a variable/parameter of a base type.

When I (as a library developer) develop a method, e.g. void Do(Foo foo), and I also provide the Foo class, I am clearly intending for you to use the Foo type I provided.

Maybe it's okay for you to inherit from Foo and alter the internal working of your Bar : Foo class. This is the most common case. If you break it, you're the one suffering the consequences of it not working.

However, there may be cases where I don't want you to be able to change how Foo works. Not so much as a matter of security (because you can also alter the state of a Foo in ways I did not intend you to by using reflection), but as a matter of not wanting you to circumvent the logic I put in Foo. Maybe because it's easy to break and I just want to protect you from yourself, maybe because I don't want you to change how it all works.

This would require me to disallow polymorphism on my Do method so that you wouldn't be allowed to pass in your Bar : Foo type, but this is a very tall order.
Alternatively, I could solve the problem by disallowing inheritance of my Foo class, so that there can never be a Bar : Foo to begin with.

Basically, when you put sealed on a class (Foo), you're stating that there exists a consumer who specifically needs Foo and explicitly does not want some variation on this type.

However, I don't understand why we actually need it

Necessity is not the criterion on which we decide which language features are allowed to exist. I don't need the += or even the ++ operator, because I can always just to foo = foo + 1. I don't need a foreach since I can always use a for or while instead.

The same can be said of access modifiers. Strictly speaking, you don't need them. All you need to do is only access your field/method in the appropriate places.
However, by having the access modifiers, I'm able to steer both my and other developers' usage of this private field/method.

This is not a security measure, by the way. A private field can still be interacted with using reflection. Access modifiers are not a wall, but they are an intentional obstacle of inconvenience to deter usage of the field/method in ways the original developer did not intend you to use them.

These features exist to make life easier for developers, not because they are strictly necessary, and the same can be said of sealed.

3
  • You can access a private field very easily by changing "private" to "public". (That's actually a lot better than using reflection). But the class author has the right to change private fields at any time, and your code breaks. Your problem. You can extend a class by removing "sealed" but that doesn't mean the extension or subclass will work.
    – gnasher729
    Commented Aug 12, 2022 at 9:28
  • @gnasher729: The point of using reflection is the ability to do so without altering the original code. But I'm not sure what you're trying to get at, as the issue isn't so much breaking changes and how to deal with them, but rather the fact that the data can be accessed even if set to private, therefore making access modifiers unusable as a security measure.
    – Flater
    Commented Aug 12, 2022 at 10:51
  • Making a variable private doesn’t mean I want to make it very inconvenient for you to access it by forcing you to use reflection, it means I don’t want you to access it at all. If you go against my intention, changing private to public is much easier. Plus I have a chance to notice this change and telling you not to access it.
    – gnasher729
    Commented Aug 13, 2022 at 12:48
0

You can always unseal a sealed class. The reverse is not true.

Reading this article (by Eric Lippert), it has four arguments as to why you should use sealed

The article does not say that you should use sealed. The article explains why so many framework classes use sealed. There is a big difference between these things.

I can see how things like readonly from C# or const from C++ actually catch genuine errors. Is there any type of error that sealed can catch?

Yes, the compiler will prevent you from using any of the sealed class' protected members, since you cannot inherit from it. In this respect it will "catch genuine errors" the same way private keeps you from making mistakes with private members.

An often cited reason for sealing a class is because, it hasn't been to designed to be extended. What does that mean? Why is it important? Are there any real examples of this?

When you unseal a class, you expose all of its protected members. This means that those members are now available to the public and have to adhere to a specified behavior. That means you will need specifications for them, documentation, and backward compatibility when you version them. In a sealed class, you can treat them as private members, which you can change more or less with impunity.

The point on compatibility isn't a reason why we need to seal classes but why it should be default.

Correct. Since it should be default, you can stop asking "Why do we need to seal classes?" Instead, you should be asking "Why do we need to unseal this class?"

The final point again is strange, because sealing your class doesn't prevent someone providing an alternative implementation in a hierarchy.

I am not sure I understand. If you seal a class, any "alternate implementation" will be a different class with no inheritance relationship.

If I didn't put enough emphasis, on it already I'm looking for reasons we actually need it. Not the usual, "someone else might write bad code". I understand that plenty of people like using sealed for design intent, but I'm looking for a practical purpose beyond that.

It saves work and simplifies things in the same way that using private or internal does. Or the way that avoiding global variables does. It minimizes logical dependencies. Scope should always be as tight as possible.

You can always unseal a sealed class. The reverse is not true.

0

If I write a class designed to be extended: I'll document how you extend it, and there will be no surprises when you try to extend it. I may help you with problems.

If I write a class not designed to be extended: You're on your own. If you try to extend it and it doesn't work, it's your problem and your problem only. I'm not going to help you. Nobody is going to help you. The only advice I will give you is: Don't try to extend it.

Worse: My class may have undocumented requirements. Your subclass doesn’t fulfil them, so you get into trouble. Worse: A requirement for my class is added. I modify the class, but you don’t know about it and don’t modify your subclass. Your subclass is now broken. If you are lucky I notice that someone removed the “sealed” and put it back, so the compiler will tell you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.