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.