12

So I wanted to inherit from a sealed class in csharp and got burned. There is just no way to unseal it unless you have access to the source.

Then it got me thinking "why sealed even exists?". 4 months ago. I couldn't figure it out, despite reading many things about it, such as:

I've tried to digest all that since then, but it's just too much for me. Eventually, yesterday I tried it again. I've scanned over all of them again, plus few more:

Finally, after lots of pondering, I decided to heavily edit the original question based on the new title.

The old question were too broad and subjective. It was basically asking:

  1. In the old title: One good reason to use sealed
  2. In the body: How to properly modify a sealed class? Forget about inheritance? Use composition?

But now, understanding (which I didn't yesterday) that all sealed does is preventing inheritance, and we can and should indeed use composition over inheritance, I realized what I needed was practical examples.

I guess my question here is (and in fact have always been) exactly what Mr.Mindor suggested me in a chat: How can designing for inheritance cause extra cost?

closed as primarily opinion-based by Euphoric, Robert Harvey, mattnz, gnat, Kilian Foth Sep 5 '13 at 7:46

Many good questions generate some degree of opinion based on expert experience, but answers to this question will tend to be almost entirely based on opinions, rather than facts, references, or specific expertise. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 5
    The question is worded quite aggressively and sounds like a rant. You should reword it if you want relevant and objective answers. – Euphoric Sep 4 '13 at 16:50
  • 11
    See Why Are So Many Of The Framework Classes Sealed? by Eric Lippert. He provides several good reasons to seal classes. – Robert Harvey Sep 4 '13 at 17:44
  • 9
    Then you didn't read the article. Go back and read it again. It boils down to this: you cannot predict the innumerable ways a client might break your class by extending it. You can blame your clients for that, but you're the one who's going to have to support them. – Robert Harvey Sep 4 '13 at 17:54
  • 4
    @Cawas He could, or you could read the article that he linked that explains it in detail. He was just summarizing that article with his comment. – Servy Sep 4 '13 at 18:01
  • 7
    Sorry, but I'm not the one who's ranting here. The answer to your question is very simple: seal a class when you don't want to support inheritance. That's it. – Robert Harvey Sep 4 '13 at 18:02
26

This is not so difficult to comprehend. sealed was created SPECIFICALLY for Microsoft in order to make their lives easier, save tons of money and help their reputation. Since it is a language feature everyone else can use it also but YOU will probably never ever need to use sealed.

Most people are complaining about not being able to extend a class and if they do then they say well everyone knows it is the developers responsibility to make it work correctly. That is correct, EXCEPT those same people have no clue on the history of Windows and one of the problems sealed is trying to solve.

Let's suppose a developer extended a .Net core class (because sealed did not exist) and got it to work perfectly. Yay, for the developer. The developer delivers the product to the customer. The developer's app works great. The customer is happy. Life is good.

Now Microsoft releases a new operating system, which includes fixing bugs in this particular .Net core class. The customer wants to keep up with the times and chooses to install the new operating system. All of a sudden, the application that the customer likes so much no longer works, because it did not take into account the bugs that were fixed in the .Net core class.

Who gets the blame?

Those familiar with Microsoft's history know that Microsoft's new OS will get the blame and not the application software that misused windows libraries. So it then becomes incumbent on Microsoft to fix the problem instead of the application company who created the problem. This is one of the reasons why Windows code became bloated. From what I've read, the Windows operating system code is littered with specific if-then checks for if a specific application and version is running and if so then do some special processing to allow the program to function. That's a lot of money spent by Microsoft to fix another company's incompetence.

Using sealed doesn't completely eliminate the above scenario, but it does help.

  • 4
    @Cawas It's pretty hard to abuse it. Eric Lippert has stated on a number of occasions he wishes that classes, like methods, were all sealed by default, unless specifically unsealed, simply because so few classes are actually designed to be inherited. It's much more likely that your class wasn't built to support it, and you should be ensuring that you have spent that dev time if you're going out of your way to mark it as unsealed. – Servy Sep 4 '13 at 18:58
  • 1
    I think if people are using sealed for their internal development software they are probably misusing it or are just wasting company time trying to be too perfect. I say probably because there are always cases that one can come up with that make sense in some particular scenario. However, for those developing library-type software, I would suspect it to be very useful for the same reasons that Microsoft decided there was a need for it. – Dunk Sep 4 '13 at 19:03
  • 1
    @Cawas The vast majority of classes written by most developers will never need to be inherited, and aren't written to support inheritance. This can quickly and effectively be conveyed to readers/users of the code by making the type sealed. If it were in fact the default option then it would mean that if someone was inheriting from a type then they would know that it's built to support it. – Servy Sep 4 '13 at 19:10
  • 2
    Unsealing a class would not mean that the class was built to support it. It just means that somebody wanted to inherit from the class and unsealed it. In the best case, using sealed will simply cause developers to rewrite existing functionality (equivalent to the sealed class) but more often than not, if the source code is available, they'll simply unseal it without taking into account any consequences. Having sealed be specifically and rarely set, is better because then the developer might want to figure out why this class is sealed. Too many sealed classes and they won't care why. – Dunk Sep 4 '13 at 19:25
  • 1
    Also security is a reason to use it! Consider a sandboxed application trying to access a file. The sandbox might test the filename-strings, but what if an attacker could send you a mutable String and then they mutate it after the security-check but before the I/O? I believe that type of scenario is why Java's String is marked final (similar to sealed) and I bet the same logic underpins some C# decisions. – Darien Sep 4 '13 at 21:32
23

sealed is used to indicate that the writer of the class hasn't designed it to be inherited. Nothing less, nothing more.

Properly designing an interface is already difficult enough for lots of programmers. Properly designing a class which can be potentially inherited adds difficulty and lines of code. More lines of code written means more code to maintain. To avoid this increasing difficulty, sealed can show that the class was never intended to be inherited, and in this context, sealing a class is a perfectly valid solution to a problem.

Imagine the case where the class CustomBoeing787 is derived from Airliner. This CustomBoeing787 overrides some protected methods from Airliner, but not all of them. If the developer has enough time and it's worth it, he can unit test the class to ensure that everything works as expected, even in a context when non-overriden methods are called (for example protected setters which change the state of the object). If the same developer (1) doesn't have time, (2) doesn't want to spend additional hundreds or thousands of dollars of his boss/customer, or (3) doesn't want to write additional code he doesn't need right now (YAGNI), then he can:

  • either release the code in the wild as is, considering that it's the concern of the programmers who will maintain this project later to care about potential bugs,

  • or mark the class as sealed to explicitly show to the future programmers that this class is not intended to be inherited, and that unsealing it and using it as a parent should be done at your own risk.

Is it a good idea to seal as many classes as possible, or as few as possible? From my experience, there is no definitive answer. Some developers tend to use sealed too much; others don't care about how the class would be inherited and the problem it can cause. Given such usage, the problem is similar to the one which consists in determining whether programmers should use two or four spaces for indentation: there is no right answer.

  • All right... Sorry if I sound aggressive again, but I just want to be clear and assertive here... I can only understand what you just wrote in either of 2 ways, if you were to write a tl;dr here. It's either "No, there is not a single good reason", or "I think there is no good reason, but I don't know as well.". To me "no definitive answer" is either one of those. – cregox Sep 4 '13 at 17:07
  • 6
    sealed is used to indicate that the writer of the class hasn't designed it to be inherited. That's your single good reason. – Robert Harvey Sep 4 '13 at 17:53
  • That's as a good reason as giving you a bicycle without lights and enforcing, somehow, you can't add lights. – cregox Sep 4 '13 at 18:08
  • 2
    @Cawas How so? In that context it's not important for the bike builder to ensure that the bike doesn't have a light, but it often is important for the user of a type to ensure that the implementation is a particular exact implementation. You enforce the constraints you need to. In some cases people may add a constraint that isn't actually needed; you have provided an example of that. Just because the constraint is mis-used in one place doesn't mean that you should never constrain anything ever. – Servy Sep 4 '13 at 18:12
  • 1
    Also security is a reason to use it! Consider a sandboxed application trying to access a file. The sandbox might test the filename-strings, but what if an attacker could send you a mutable String and then they mutate it after the security-check but before the I/O? I believe that type of scenario is why Java's String is marked final (similar to sealed) and I bet the same logic underpins some C# decisions. – Darien Sep 4 '13 at 21:29
4

When a class is sealed it allows code using that type to know exactly what they're dealing with.

Now in C# string is sealed, you can't inherit from it, but let's assume for a second that that wasn't the case. If you did, then someone could write a class like this:

public class EvilString// : String
{
    public override string ToString()
    {
        throw new Exception("I'm mean");
    }
    public override bool Equals(object obj)
    {
        return false;
    }
    public override int GetHashCode()
    {
        return 0;
    }
}

This would now be a string class that is violating all sorts of properties that the designers of string knew to be true. They knew that the Equals method would be transitive, this is not. Heck, this equals method isn't even symmetric, as a string could be equal to it but it wouldn't be equal to that string. I'm sure there are all sorts of programmers who have written code under the assumption that the ToString method of string won't throw an exception for non-null values. You will now be able to violate that assumption.

There are any number of other classes we could do this for, and all sorts of other assumptions that we could violate. If you remove the language feature for sealed then the language designers now need to start accounting for things like this in all of their library code. They need to perform all sorts of checks to ensure that extended types of the types they wish to use will be written "properly", which can be a very expensive thing to do (both in dev time, as well as at run time). By being able to seal a class they can ensure that this isn't possible, and that any assertions they make about the implementation details of their own types can't be violated, except for those types that are intentionally designed to be extended. For those types designed to be extended you'll find the language code needs to be much more defensive about how it deals with them.

  • But you are able to violate that assumption in your code. It's not like you're grabbing String's source and editing it. The designers wouldn't need to account for this because it's 1 leaf, 1 branch, 1 client using it at its own risk and discretion. It won't disseminate from there, unless other clients prefer to do so. And so on. – cregox Sep 4 '13 at 18:05
  • 3
    @Cawas And if an exception is throw at an unexpected time and a resource is leaked as a result, or a class is left in an indeterminate state and functions inappropriately? This case is a bit silly, but it could easily be the case that someone writes an implementation that they think is sensible, but occasionally throws when it shouldn't, or doesn't work with certain values. They'll then complain when the language code doesn't work. It's preferable to just not let people do things that the language won't be able to support. – Servy Sep 4 '13 at 18:08
  • In any case you are talking about building a compiler. There are many such sealed features built-in there that's not exposed for us to use. It still doesn't explain why use it outside such a narrow scope and, being as narrow as compiler code, doesn't even explain sealed existence. – cregox Sep 4 '13 at 18:15
  • 2
    @Cawas The string type isn't compiler code. It's part of the language code. Completely different. In any case, I just picked one example. You could pick up most any sealed class in the entire BCL and use that as an example. – Servy Sep 4 '13 at 18:17
  • 4
    @Cawas: I think what you might be missing here is the customer support aspect. If you allow a class to be inheritable, customers are going to inherit from it, and then they're going to call you when it breaks. You can tell them all day long that they inherit from that class at their own risk, but you're still going to get the call, because you allowed them to inherit from it, so they assume that it is safe to do so. – Robert Harvey Sep 4 '13 at 18:20
3

Is there any good reason to use sealed?

Enh? Not often. I will only used sealed when I have an immutable type (or some other limitation) that really really needs to remain immutable and I cannot trust inheritors to furfill that requirement without introducing subtle, catastropic bugs into the system.

Suppose there is a good reason to use sealed and we should use it as default for everything, what we do with inheritance?

We use inheritence for those things that are not default. Even if composition is favored over inheritence (and it is), that doesn't mean inheritence is to be discarded. It means that inheritence has some intrinsic problems that it introduces into design and code maintainability and composition does much of the same thing with (in general) less problems. And it means that even if composition is favored that inheritence is good for certain subsets of problems.

I don't mean to be too mean but if you've just now heard of SOLID and unit testing, maybe you should get out from underneith your rock and actually write code. Things aren't clear cut, and rarely will "always do X" or "never use Y" or "Z is best" be the right way (like it seems you gravitate towards based on your question's views). As you write code you can see cases where sealed could be good and cases where it causes you grief - just like any other tradeoff.

  • To me, this and the "to avoid having to support inheritance to clients" that Robert don't want to elaborate are the most promising answers... Maybe you could elaborate more on those cases where you do use sealed, please? – cregox Sep 4 '13 at 18:17
  • @Cawas - I'm not sure I can well. I very rarely do it, and it's often a tradeoff where the guarantee that something stay immutable (to make performance optimizations, design simplification) is worth the inability to inherit from the object. – Telastyn Sep 4 '13 at 19:33
  • That's cool. Dunk already dunked it! :P Thanks so much for the answer. And you were the only one to pick up my "subtle hints" about my expertise. Seems like people were assuming I knew more, I don't know... But I wish "just coding" would take me out of this rock. Maybe I do apply those concepts in some way, just not as procedurally as I probably should. – cregox Sep 4 '13 at 19:37
2

I think this question doesn't have objective answer and that it all depends on your perspective.

One side, some people want everything "open" and burden of ensuring everything is working correctly is on person who extends the class. This is why classes are open to inheritance by default. And why Java methods are all virtual by default.

On the other side, some want everything to be closed by default and to provide options to open it. In this case, it is base class's author who needs to ensure everything he makes "open" is safe to use in any way imaginable. This is why in C# all methods are non-virtual by default and need to be declared virtual to be overriden. It is also for those people, that need to be way to circumvent "open" defaults. Like making class sealed/final, because they see someone deriving from the class a big problem to their own class's design.

  • This is more like it! This might be a good reason... "Because people want to close it". And it's also what I'm trying to say in the question: looks like non-virtual classes in C++ can be overridden. sealed can't. How can we inherit from them? All I read is we can't. Ever. I don't know. I've been back and forth on this subject for 4 months now, since I first heard about sealed. And I still don't know how to do it right when I need to modify anything on a sealed class. So, I see no "option to open it"! – cregox Sep 4 '13 at 17:28
  • 4
    This is not why C++ class methods are not virtual by default. Making a method virtual means the there has to be additional overhead on the method lookup table, and C++ has the philosophy that you only pay for overhead when you want the feature. – Michael Shaw Sep 4 '13 at 17:31
  • @Ptolemy Ok, I removed it. I should have never written it, because I knew people start complaining about it. – Euphoric Sep 4 '13 at 17:35
  • hey, I completely appreciate that if you want to claim that people want to write closed classes by default, and have to actively choose to open them then you are going to have to clutch at straws – Michael Shaw Sep 4 '13 at 17:38
  • IMO it's extremely similar to the private/protected/public debates when it comes to APIs: It strongly depends on who controls the class, who wants to inherit the class, communication between the two, and how often new versions come out. – Darien Sep 5 '13 at 17:29
2

First of all, you should read Eric Lippert's post on why Microsoft seal their classes.

http://blogs.msdn.com/b/ericlippert/archive/2004/01/22/61803.aspx

Secondly, the sealed keyword exists to do exactly what it does - prevent inheritance. There are many situations where you want to prevent inheritance. Consider a class that you have a collection of. If your code depends on that class working in a particular fashion you don't want someone overriding one of the methods or properties in that class and causing your application to fail.

Thirdly, using sealed encourages composition over inheritance, which is a good habit to get into. Using composition over inheritance means that you can't break Liskov's substitution principle and you're following the Open/Closed principle. sealed is therefore a keyword that assists you in following two of the five most important principles of o-o programming. I'd say that makes it a very valuable feature.

  • First part is already pointed out in the question comments. Second part is just more reasoning without real causations. Third part, even though already approached elsewhere, might have something to it. I'd focus on that one. – cregox Sep 5 '13 at 16:19
  • the third point doesn't encourage composition over inheritance, it completely eliminates inheritance as an option, even if that was the best overall choice. – Michael Shaw Jun 2 '16 at 11:21
  • That depends if you control the codebase or not. If you seal classes you can always unseal them later if needed. – Stephen Jun 2 '16 at 22:02
-2

the problem with sealed in C# is that its rather final. In 7 years of C# commercial development, the only place I have seen it used is on Microsoft .NET classes where I feel I had a legitimate reason to extend a framework class to implement adjusted behaviour, and because the class was sealed, I could not.

It is impossible to write a class thinking about every possible way it could be used, and deciding that none of them are legitimate. Likewise if framework security is dependent on the class not being inherited, then you have a security design flaw.

So, in conclusion, I'm firmly of the view that sealed serves no useful purpose, and nothing I have read here so far has changed that view.

I can see there have so far been three arguments that justify sealed placed so far.

  1. Argument 1 is that it eliminates a source of bugs when people inherit from classes and then have increased access to the internals of that class without really understanding the internals properly.

  2. Argument 2 is that if you extend a microsoft class and then microsoft implement a bug fix in that class but your extention relied on that bug then your code is now broken, microsoft gets the blame, and sealed prevents that

  3. Argument 3 is that as the developer of the class, its my choice whether to allow you to extend it, as part of my design of the class.

Argument 1 seems to be an argument that you the end programmer don't know how to use my code. That may be the case, but if you still allow me to access the objects interface it still holds true that I am using your object, and making calls to it without understanding how to use it, and so can make just as much damage that way. This is a feeble justification of the need for sealed.

I understand where the factual base for arg 2 comes from. In particular when microsoft put additional OS checks on win32 api calls to identify malformed calls, which have improved the general stability of the Windows XP compared to Windows NT, but at the short term cost that many apps were broken when Windows XP was released. Microsoft resolved this by putting 'rules' in for these checks to say, ignore when app X breaks this rule, its a known bug

Argument 2 is a poor argument because sealed at best makes next to no difference to this because if there is a bug in the .NET library you have no choice but to find a work around, and when the microsoft fix comes out, its quite likely means that your work around which depends on the broken behaviour is now broken. Whether you worked around this by using inheritance or by some other additional wrapper code, when the code is fixed, your work around will be broken.

Argument 3 is the only argument that has any substance to justify its self. But it is still weak. It just goes against the grain of code reuse.

  • 2
    /me sends an email to microsoft, please can you unseal class X for me and ship out a new release of .NET. kind regards,.... Like I said, the only time in commercial development I have seen a sealed class, it was not possible to unseal it. – Michael Shaw Sep 4 '13 at 17:46
  • 6
    It is impossible to write a class thinking about every possible way it could be used -- That's precisely the reason many Framework classes are sealed... It eliminates the need to think about every possible way someone might extend the class. – Robert Harvey Sep 4 '13 at 17:47
  • 5
    If you could unseal it, there wouldn't be much point in sealing it, would there? – Robert Harvey Sep 4 '13 at 17:48
  • 2
    @Ptolemy Allowing people to extend your class is a feature. One that consumes a significant amount of development effort. It's a feature only worth justifying if the author of the class can see enough benefit in potential extensions to justify the effort. If they can't justify the effort, and therefore the type and its users aren't built to support extension, then it's important to prevent it, otherwise you'd be technically able to extend the type, but it's unlikely to work properly when you do so. – Servy Sep 4 '13 at 17:55
  • 2
    @Cawas: You're just being difficult. – Robert Harvey Sep 4 '13 at 17:59
-7

I only use sealed keyword on classes that contain only static methods.

  • 9
    Then why not just mark the class as static instead? – Servy Sep 4 '13 at 19:28
  • @Servy... Good point. – Vladimir Kocjancic Sep 4 '13 at 19:31
-8

As evident by my question, I'm no expert here. But I see no reason for sealed to even exist.

It seems to be made to help code architects to design an interface. If you want to write a class that will go out in the wild and many people will inherit from it, modifying anything in that class can break it for too many people. So we can seal it and nobody can inherit. "Problem solved".

Well, looks like "covering a spot and uncovering another". And it's not even solving a problem - it's just eliminating a tool: inheritance. As any tool, it has many usages and inheriting from a unstable class is a risk you take. Whoever took that risk should know better.

Maybe there could be a keyword stable, so people would know it's safer to inherit from it.

  • 2
    "Problem solved" -- Exactly. – Robert Harvey Sep 4 '13 at 17:48
  • @RobertHarvey It's not solved, as explained right in the next paragraph. – cregox Sep 4 '13 at 17:50
  • 8
    You're just mad because you can't extend the class. That doesn't mean that sealed is not a useful tool to the framework designers. Classes in a framework should be black boxes; you should not have to know about the internals of a class to be able to use it properly. Yet that's exactly what inheritance demands. – Robert Harvey Sep 4 '13 at 17:51
  • I never said it's not useful. I say I don't see its usage, and point out why. Please, I'm begging you, give me a good reason to use it! Even if it's just "because I want to follow a closed design pattern", that's fine. I don't see anyone giving that reason, explicitly. – cregox Sep 4 '13 at 17:55
  • 7
    The good reason is that you don't have to support inheritance on a sealed class. – Robert Harvey Sep 4 '13 at 17:59

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