Fog Creek Software
Discussion Board




Sealed Classes C#

Does including the override keyword in front of a method allow you to inherit from a sealed class?  I've included the two examples from ms-help:

Java:
class myPoint extends Point {
  public String toString(){
      return "myPoint";
  }
}

Equivalent C#:
class myPoint:System.Drawing.Point {
  public override System.String ToString(){
      return "myPoint";
  }
}

In my classes that are trying to inherit from a sealed class (OleDbConnection), can I just put override in front of each method?  Is it as simple as that?

nathan
Thursday, January 22, 2004

No you can't derive from a sealed class. It's sealed.

Dennis Forbes
Thursday, January 22, 2004

Dennis - that's my understanding as well.  So why does ms-help say that using the override keyword makes it equivalent code?

I guess they're just showing an example of what you can't do?

nathan
Thursday, January 22, 2004

What precisely does the help say that is misleading you?

Override is used to declare, well, overrides of inherited virtual class members, but you can't inherit class members if you can't derive from the class in the first place.

Dennis Forbes
Thursday, January 22, 2004

It says to correct this error (The <classname> class is marked as sealed) examine your code and determine how best to convert the class.

Then it gives an example of Java code that extends Point.
Then it gives "Equivalent C# Code" that attempts to inherit from Point.

I see now it's not an example of how to fix it, it's an example of what's wrong.

Thanks.

nathan
Thursday, January 22, 2004

Sealing a class means you can't derive from it.

Sealing a method means you can't override it.

That's pretty much it. There's no way around the rules that I've ever heard of. The "override" keyword is for virtual methods, not sealed methods (or classes).

Brad Wilson (dotnetguy.techieswithcats.com)
Thursday, January 22, 2004

As a sidenote, it's a real PITA that Microsoft chose to seal some of the data classes (i.e. like the poster I wanted to derive from a connection object to hold some additional connection state information), and I've yet to see a reasonable explanation about why they limited flexibility in such a way.

Dennis Forbes
Thursday, January 22, 2004

There's a school of thought that says you should spend time explicitly thinking about the impact of what happens when your class becomes a base class. The means, no "protected" or "virtual" until you fully understand the implications.

One could argue that sealing a class is part of this school of thought. I'm not saying it's right, but giving a possible explanation.

Brad Wilson (dotnetguy.techieswithcats.com)
Thursday, January 22, 2004

When a library class is "sealed" there's likely so much black magic going on inside that you wouldn't be able to derive your own class and actually get it working.

To test this theory, you could just grab Reflector and study the class in question.

Chris Nahr
Thursday, January 22, 2004

I think the team does follow the "sealed by default" philosophy.

Just me (Sir to you)
Thursday, January 22, 2004

http://www.sellsbrothers.com/news/showTopic.aspx?ixTopic=411

SpacemanSpliff
Thursday, January 22, 2004

They sealed way more things than I would've liked.  I've ran into this limitation a few times.  My guess is that they sealed so many either because the interop code in the background didn't allow for the flexibility of non-sealed or they benchmarked and found that the sealed classes performed sufficiently better to warrant losing the flexibility.

SomeBody
Thursday, January 22, 2004

Rather than following up here, I'll post a blog entry about this later today.  See http://blogs.msdn.com/ericlippert.

Eric Lippert
Thursday, January 22, 2004

Note that Eric' comment got the trailing period included in the link (damn unlicensed Fogcreek programmers!), so prune it off. i.e.

http://blogs.msdn.com/ericlippert

Hrmm....I wonder...recursive link...

http://discuss.fogcreek.com/redirect.asp?http://blogs.msdn.com/ericlippert

Dennis Forbes
Thursday, January 22, 2004

Whoops, sorry about that.

Let me give you a future-proof link as well:

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

Eric Lippert
Thursday, January 22, 2004

eric--

you mention a bunch of good points, but some of the comments on Chris Sell's blog raise another good point:

why do so many framework methods handle only objects of a particular class, not those which handle a particular interface?

the only thing you mention which strongly applies is security, though apparently you can trick the system into believing your CFooEvil class is in fact a CFoo class, so that's not necessarily valid.

was there some lesson learned from COM (everything should use an interface) which made people turn the other direction?

mb
Thursday, January 22, 2004

"only someone with the MSFT private key can subclass it"

eric
How exactly is this implemented?

coresi
Thursday, January 22, 2004

Thanks for the great summary, Eric, but I must take issue with quite a few things you've stated.

"Typical developers say "IS-A-SHMIZ-A, I just want to slap a Confusticator into the Froboznicator class".  That developer could write up a hash table to map one to the other, but then you have to worry about when to remove the items, etc, etc, etc -- it's not rocket science, but it is work."

No, most typical developers thinks OOPishly, and maybe there is a connection specific attribute that they'd like to correlate with each connection, so now they want a MySqlConnection that subclasses SqlConnection with the addition of the new property (and of course classic connection recipients can use it polymorphically without change -- this was precisely what I wanted to subclass it for). This is _absolutely_and_ postively_ the whole foundation and purpose of OOP, so claiming that it's some sort of lazy way out just sounds, well, weak and contrived as some sort of last ditch defensiveness. I know that you're exaggerating, but I'd bet that the vast majority of "irritated that it's sealed" attempts at using framework parent classes were for academically and logically correct OOP reasons, rather than the crazies that want to stick a bunch of crap in that you imply.

Regarding testing and stability, that too seems like a weak defense -- Obviously if someone subclassed your class, their class is only going to be created explicitly (it's not going to invade the system). The security claim is a grand hand waving "these are not the classes you are looking for" distraction, but explain a scenario where this would risk security in a properly designed system? I can think of a lot of _hacks_ where it would break the fragile stability of the system, but not a single credible scenario (speaking of hacks, there are hacks out there to unseal classes in the IL...I hope you aren't seriously claiming that sealed is a security defense).

Sorry if this is a bit strong, but I took issue with the claim that it's the grand tradition of OOP that drives the use of sealed, and it's lazy, non-OOPish programmers deriding it. I think you flipped the roles.

Dennis Forbes
Thursday, January 22, 2004

> was there some lesson learned from COM
> which made people turn the other direction?

That's a big question which I'm not really sure how to answer.  It's sort of an "apples vs. oranges" thing -- obviously COM was designed to be compatible at a binary level with C++ style implementation inheritance.  Can you rephrase the question?

Eric Lippert
Thursday, January 22, 2004

> How exactly is this implemented?

Do you mean "how do you create a class that cannot be subclassed except by people with a certain key?"  See

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconInheritanceDemands.asp

Or do you mean "how did the CLR security team implement inheritance demands in the framework?"  I don't know -- I've never read that particular bit of the source code. You could find the Rotor source code and see for yourself I suppose...

Eric Lippert
Thursday, January 22, 2004

> No, most typical developers thinks OOPishly

Perhaps your definition of "typical" is different than mine.  I'm basing my comments on the rather large amount of research we did into how VB6 developers, script developers, web developers, etc -- who outnumber Java, C# and C++ developers by a rather considerable margin -- use developer tools.  If you want to limit "typical" to professional developers with CS degrees and/or lots of industry experience with OOP languages then sure, I'll agree with the tautology that OOP developers frequently use OOP techniques.  OOP-savvy developers are atypical in my world.  My developer customers are the guys recording macros in Excel and then tweaking the recorded VB.

> explain a scenario where this would risk
> security in a properly designed system?

I'm not exactly sure what you're asking me to do here.  In my opinion, a "properly designed system" is one in which one can reason about behaviour of security-sensitive data based on type information.

If you're asking me to give you an example of a system which has holes that can be mitigated easily by sealing classes, that I can do.  If you're asking me to give an example of a system that has holes that can be mitigated ONLY by sealing classes, that I cannot do. 

I intend to do a blog entry at some point on security issues surrounding unsealed classes, so stay tuned.

Eric Lippert
Thursday, January 22, 2004

> was there some lesson learned from COM
> which made people turn the other direction?

COM says, "everything must be accessed only by interface". (or so I believe)

i mean, sure, you can write a program which will take in a string and always cocreate a particular class (clsid) and initialize it with a string (or moniker), but generally when an object needs another object, it requires a particular *interface* (abstract type), not a *class* (concrete implementation derived from this type).

There's lots more to COM than that (things like the binary structure of what an interface is, or how particular well-known interfaces work, etc), but this is the relevant aspect of it for this discussion.

mb
Thursday, January 22, 2004

I've got to say that I think that the .NET team took things a little too far in one direction.

It's a reasonable decision to seal a class if you don't know how deriving from it is going to work. While I don't agree with the decision, I can at least understand the rationale.

However, if you do that, you should think about having methods that use that class take an interface, not a concrete class. That way, if someone want's to write a wrapper class that lets them keep their state information with the database connection, they can do it.

I admit this approach has issues too (runtime overhead of using interfaces, and wrapping stuff is a PITA). However, database access would seem to be a reasonable place to use it (the overhead of your SQL query is almost certainly going to dwarf the overhead of the extra indirections for interface calls).

Sum Dum Gai
Thursday, January 22, 2004

Indeed, I have often myself wondered why the designers of particular portions of the framework chose to define base classes instead of interfaces.

"Stream" is an abstract base class, and lots of methods take Stream objects as arguments.  Why isn't it an interface?  Compare that to the myriad collection interfaces -- IList, IEnumerable, etc, etc.  With the collection interfaces the framework provides base class implementations, but most of the methods take interfaces, not base class types.

Now, let me go on the record right now as saying that I didn't design any of this stuff -- I'm just making some educated guesses here.  Take 'em with a grain of salt.

What strikes me first with Stream as a base class rather than an interface is that this guarantees that _all_ stream implementations are MarshalByRef.  It seems reasonable that the framework class library should _insist_ that streams (which after all, could be used to marshal serialized state around) should be agile with respect to appdomains.

Collections on the other hand do not have any such deep implementation constraints.  And furthermore, there are so many different ways to slice and dice a collection, it makes sense to have a panoply of interfaces which you may or may not choose to implement on a particular collection. 

Interfaces describe "I can do" contracts.  Base classes describe "I am a" contracts. I have not done a thorough review of the framework class library by any means, but I strongly suspect that if I did, we'd discover that many of the extensible base classes are extensible classes rather than interfaces because at a low level, the framework depends on knowing what a thing IS, not what it CAN DO.  All the reflection base classes, for example, actually have implementations tied deep into the internals of the type system.

Someone asked about lessons from COM.  Here's one: as a guy who has debugged OLE Automation code far more than any mortal ought to, I know that there are reasons why so few people implement their own ITypeInfo, IDispatch, etc.  It's _ridiculously_ difficult to do right.  The underlying work of implementing a general-purpose late-bound type system is just too hard. 

That's why OLEAUT provides so much goo to provide default implementations of these interfaces that you can then use -- in COM, a static constructor function is the closest thing there is to a base class implementation of an interface.

Now, don't get me wrong -- subclassing the CLR Type class is no picnic either.  See the doc if you don't believe me.  But it is orders of magnitude easier than implementing a generalized ITypeInfo from scratch.

Now consider the care with which the Type object had to be designed and implemented insofar as its extensibility features were concerned.  That was not cheap or easy, but its worth it because a type system extensible by third parties is a _core_ CLR scenario. 

Could we apply the same care to every class we implement, even the ones that are nowhere near "core" scenarios for extensibility?  Sure, we could -- and we could ship the CLR a couple years later because of it, or nglect other important features. 

It's an old story.  Every possible feature a developer could want, high reliability, ships on time -- pick any two but you don't get all three!  The framework designers focussed on the last two and I think that was a good choice.

The simple fact is that software of this magnitude is _insanely_ complex. OOP design was invented to MANAGE complexity, not INCREASE it.  Sealing classes that don't need to be extensible, and using base classes instead of interfaces are both techniques for managing complexity by decreasing the number of potential usage scenarios that must be designed for and tested.  That's the kind of pragmatism we've got to have to ship any code at all. 

I think that's a pretty reasonable set of reasons, but hey, you guys are welcome to disagree if you still think I'm grasping at straws.

Eric Lippert
Friday, January 23, 2004

So then what is the justification for "Control" and "Page", instead of "IControl" and "IPage" in ASP.NET?

Brad Wilson (dotnetguy.techieswithcats.com)
Friday, January 23, 2004

I haven't the faintest idea what the specific design criteria were -- heck, I've never written an ASP.NET page more complex than "hello world".  You'll have to ask someone who designed that class library.

Eric Lippert
Friday, January 23, 2004

Thanks Eric, very useful link.
I have to admit that it is the first time I hear about ‘Class Inheritance Demands’.

coresi
Saturday, January 24, 2004

Seems to me this all started because somebody wanted to decorate a connection class and felt that it being sealed was a limitation in some way.  Where's the limitation with a sealed class?  You should be creating adapters anyway and decorating the concrete adapter class that delegates to the sealed class.  That puts you in control of what the consuming application expects.  If the platform underneath changes, you need only change your concrete adapter and the software remains the same.  Isn't this one of the touted benefits of OO design and interface based programming?  Just seems to me this is a non-issue in the scheme of things.

Roland Rodriguez
Friday, March 12, 2004

"Not sealed" classes provide access to all of the protected members of their base classes.

Since such access is a bad thing for library classes, library class writers have to either not use inheritance or use sealed.

Andy Freeman
Saturday, April 10, 2004

*  Recent Topics

*  Fog Creek Home