Please, no! This shouldn't be the default - it's a constant bugbear of mine where I want to extend a class from a library, and I can't because it's been sealed for no good reason.
It is a general design principle for .net libraries that you should seal a class unless you deliberately and carefully design it to be extensible.
If a class is unsealed you are basically not able to change it ever again, since someone might have subclassed it and depend on all kinds of internal behavior. So it should only be done very deliberately.
This isn't just a design principle for .NET libraries, it's a SOLID design principle. All classes should be inextensible unless they are explicitly designed to be extended.
The open/closed principle doesn’t say that. It says classes should be “open for extension” (usually meaning you are able to inherit from them and add functionality) and “closed for modification”, meaning you aren’t able to change internal implementations.
It does not say “everything should be closed by default unless explicitly marked otherwise”...
Composition is still an option, and arguably the default option. Sealing a class does not prevent anyone from extending the functionalities offered by a class.
> It is a general design principle for .net libraries
Is it? A more typical approach, IMO, is for internal classes to be marked as `internal`, and for `public` classes to be considered part of the public API.
You do both. If you don't need to expose a class you keep it internal. If you do need to expose it, you keep it sealed unless you deliberate design it for extension.
The overarching principle is to keep the API as tight as possible, because as soon as an API is public, you have committed to it and even subtle changes in observable behavior may break some client.
Usually you define an interface for anything that you want to be overridable and provide some way for consumers to provide their own implementations. Then seal your own implementations.
Then you only need to design your interfaces in such a way that they can easily be implemented by composition.
This is the approach taken in ASP.Net and in WCF; although the latter does a really bad job at making their interfaces user friendly (for even tiny changes you have to re-implement a lot of functionality - which could have been avoided by better interface design).
How do you know it's not been done for a good reason? Designing classes for extensibility is not easy. Furthermore, you can make classes extensible later on, but you can't do the reverse without breaking the API.
Designing for extensibility is not easy in the context of virtual methods. Every virtual method can be overridden, which means that you effectively need to define contracts for all your methods that can be overridden, and only call them in ways that respect those contracts. In a language where everything is virtual by default, like Java, this means all non-private methods. Which is not good, because most of the time, the reason why you're making a method public is to allow calling it, not to allow overriding. That's why C# made "virtual" explicit opt-in.
But for inheritance, there's no such issue. If someone inherits from your class, they can't break your invariants - they still have to invoke your constructor, and they don't have access to any private state. In C#, they also can't override public and protected members, unless those are declared virtual. Thus, there's no safety or correctness reason to prohibit derivation.
It should also be noted that there's no perf gain from declaring a class "sealed", unless it has virtual methods (normally, inherited from a base class, because it doesn't make any sense to have virtual methods declared in a sealed class). Thus, the only time to do so is when you have a class hierarchy, for leaf nodes in that hierarchy.
It cannot, really. It can only shadow the old declaration (and isn't even necessary). This means if you have Derived : Base with such a shadowed member, then code from the library that uses a Base cannot call your shadowing member. You can't really change behaviour that way, nor break anything, except for yourself.
Sometimes yes - what I'm saying is that it shouldn't be the default, and all too often is, for no good reason, even in classes that would appear to be natural extensibility points.
On the plus side, when I encounter this, I generally ask the author on GitHub if it can be 'unsealed', and they generally do so.
Because it is bad design known as fragile base class, which usually leads to hard to track down bugs, because someone somewhere is accessing methods or internal class data structures that they shouldn't have to in first place.
That sounds more like an argument to seal your classes, rather than for why the JIT doesn't, in addition to that, assume classes as sealed until proved otherwise.
You're talking about the programming model, where I'm talking about the implementation.
Well, JIT heuristics end up being designed to live with the typical code patterns of the programming language.
In Java's case, since methods are virtual by default so with open classes a large majority of the code has a possible indirection, hence such optimizations.
I had the same thought when I saw that. There seems to be a trend to seal and lock down classes preventing any kind of extension. It sort of misses one of the main benefits of OOP and I know what I'm doing.
As a consumer of library software, yes, you know what you are doing and you know how you want to override things. That is true. But the advice is more about how to create stable libraries that can change over time and making sure the public surface area and extensibility points are created deliberately and can be maintained. It means the lib developer can release their software knowing that he didn't make any breaking changes to the public surface area of the library.
The problem is, some libs are not well maintained, and not that well designed, and you (the generic "you" ) are happy to deal with breaking changes if and when they come along on version upgrades. It can be frustrating if they essentially locked it all down without much thought. But in general, I think the sealed by default is the better advice. You can do cunning ( evil ) things to get around it.
I understand the argument for composition-of-inheritance but they rarely apply when you actually need to do it. Often it's to reach in and fix a bug or enhance the behaviour of an existing component. You simply can't do that with composition.
Without inheritance (if the class is sealed) I often end up having to re-write the entire component or simply accept my fate. So that is a lose-lose situation.
That's where delegation comes in. You wrap each public method of Foo in another class call MyFoo and then fix the one method you care about. With C# and R# it's a simple matter of:
Create a new class
Create a private variable:
private Foo _foo
Click on _foo
Resharper menu -> Generate Code -> create Delegating members.
But you can't pass that into places that accept Foo because MyFoo isn't of the type Foo so that's a non-starter is most cases.
Secondly this code generation solution is just re-implementing inheritance again poorly and with the above mentioned limitation. I fail to see how code generating a proxy is any way better than (or significantly different from) inheritance.
So if you implement an interface and then use code generation to create proxy from a "parent class", congratulations you just reinvented inheritance. What's the difference?
> There is principle, which sounds like "favor object composition over class inheritance".
This is begging the question, isn't it? Why favor object composition over inheritance? The qualification here that's missing is "for code re-use". If your goal is simply to re-use code then you should favor composition. But if you're actually modeling an is-a relationship or trying to modify the behavior of a base class then that's not just for code re-use.
This subtly seems to have been lost. It's much easier to religiously ban all inheritance than to simply use it appropriately.
> In most cases "HAS-A" relationship is more semantically correct than "IS-A" relationship between classes.
In most, but not all cases. You will find that in most cases of inheritance in frameworks, for example, follow the is-a relationship perfectly. And you'll find most of time composition is used for has-a relationships. It's an error to mix this up but that's not the fault of inheritance.
For example, in my own software I inherit from the database context class to provide a host of features beyond what the framework provides. My context is a database context.
> Composition is more flexible than inheritance.
If you need that flexibility, great. But I don't see why having more flexibility is automatically good for the correctness of the program. If X is always implemented by Y, that's much easier to reason about.
> Inheritance breaks encapsulation.
I don't see how that's true. Your base class exposes what it wants to potential child classes so encapsulation is preserved. There are some languages that provide no limits (no protected/private/virtual/etc) but then those don't provide any encapsulation in any other situation either.
> A design based on object composition usually will have less classes.
Says who? What's the research behind this? I assume most projects have a mix of inheritance and composition as needed.
> It is possible to implement "multiple inheritance" in languages which do not support it by composing multiple objects into one.
That's not multiple inheritance -- it's just composing multiple objects using some kind of proxy. There is a difference.
> There is no conflict between methods/properties names, which might occur with inheritance.
Please, no! This shouldn't be the default - it's a constant bugbear of mine where I want to extend a class from a library, and I can't because it's been sealed for no good reason.