> > So, are you in the "no, not all methods should be virtual" > > camp? If so, can't you see that having classes unsealed > > by default is equally dangerous. > > No I'm not in the 'all methods should be virtual' camp. But > I think the issues are rather different for methods. Firstly, > there is the performance question and the fact that you > cannot inline virtual methods.
Actually you can. Try saying that you can't on DevelopMentor's ADVANCED-JAVA list (on the same LISTSERV as this), and see how long it is before Jeff Kesselman jumps on you. JVMs have been doing what's called monomorphic inlining of virtual methods for some time now. This is a technique where the JVM detects that you are not exploiting polymorphism, and behaves as though methods weren't virtual. (It also adds checks to make sure that if you do start exploiting polymorphism it will re-JIT the relevant code.) Of course methods are virtual by default in Java, so there was more incentive to develop technology that could inline virtual methods. But it does illustrate that there aren't necessarily any perf advantages to non-virtual functions. > Secondly, if you make all methods virtual, then you > lose the ability to make subtle decisions > about how the class should be inherited. eg. if you have > a Shape class designed for people to inherit from so > that they can draw different single-color shapes, you'd probably > choose to make the Draw() method virtual, but not the > Color property. But doesn't the exact same argument you've been using against sealed also apply here? Why prevent people from doing what they might think they want to do in inheritance scenarios? (I think it's a specious argument in this case, but I think it is in the class case too.) > Of course you'll say that in that example I've taken > the trouble to design the class for overriding. If > I've understood you correctly you want a class to be > sealed unless you've specifically taken the trouble to > design it for inheritance. That's about the size of it. It's just a safe-by-default thing. Inheritance is definitely in the category of things you should only do if you really mean it. Although as I said, this idea of making 'sealed' the default is not actually what I think, it's just that I've found myself moving more towards that opinion I think there are pros and cons to having classes sealed by default and unsealed by default. > I think my reply would be that even if you haven't spent > any time thinking about inheritance, then at least other > people should by default have the chance to inspect your class > and decide, given its public and protected methods whether > they can usefully inherit from it. Again, surely you could apply that argument to virtual functions, where it is manifestly wrong. If you haven't given any thought to how someone should go about overriding a function, then the chances are they're not going to be able to do it - they'll have no idea what the right semantics are: should they call the base class, and if so before, during or after their own call, and what invariants is the method supposed to uphold? And those are just the straightforward ones - if you start getting into things like lock semantics in a multithreaded environment it doesn't even bear thinking about. > You might not think they can do much with it but > other developers will _always_ > come up with ideas you haven't thought of. Which is precisely what troubles me. The general rule with code is that if you don't test it, it probably doesn't work. So if someone derives from my class to use it in a way I haven't thought of, I won't have constructed a unit test for that scenario, so there's a considerably better than evens chance of it not working. Unless they're bolting on entirely orthogonal functionality. In which case that sounds like a design error, and, more importantly, nothing they couldn't have done without inheritance. Inheritance is not appropriate for utility functions, or for composition of code. > I think I'd find the arguments that you and Mike are > putting for the dangers of inheritance a lot more > convincing if you could come up with a concrete > example of how you can break a class by deriving > from it That's a slight mischaracterisation of my argument - it's not breaking of the class itself that really worries me. But this might be close enough to keep you happy - it's an example of something else breaking because of a class being derived from: public class MyFoo { private int spongVal; public int Spong { get { return spongVal; } set { spongVal = value; } } public override string ToString() { return spongVal.ToString(); } } along comes a well-meaning developer who derives from this thus: public class MySubvertedFoo { private string nameVal; public string Name { get { return nameVal; } set { nameVal = value; } } public override string ToString() { return string.Format("{0}, '{1}'", base.ToString(), nameVal); } } So far so good - doesn't look like MySubvertedFoo has broken anything - just added a new property, and has modified ToString. But what's this? Back in the first assembly we find this: public MyControl : Control { // Oo look! Inheritance, only good ;-) [TypeConverter(MyFooConverter)] public MyFoo Foo { get ... set ... } ... } public MyFooConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext c, Type t) { if (t == typeof(string)) return true; return base.CanConvertFrom(c, t); } public override object ConvertFrom(ITypeDescriptorContext c, CultureInfo ci, object o) { if (o is string) { string s = (string) o; int spong = int.Parse(s); MyFoo f = new MyFoo(); f.Spong = spong; return f; } else return base.ConvertFrom (c, ci, o); } } So, what happens if somebody innocently does this: myControl1.Foo = GetAMyFoo(); That will compile. And it's not particularly unreasonable. But what if GetAMyFoo() actually creates a MySubvertedFoo? Well it might work. But if someone manages to use it in the Visual Studio .NET Forms Designer, (which would probably require the line of code above to be in the constructor of a form from which someone else derives, in order to get that line of code to execute in the designer) they would get a FormatException if they tried to edit the Foo property of the control. (And the same would happen if they were using the PropertyGrid outside of VS.NET. Or any other piece of code which relies on the TypeConverter infrastructure.) So, we didn't break the class. We broke something else that was relying on non-obvious semantics of the class. OK, so it's a bit obscure. But then that's the point. And it's not unrealistic. We hit problems because of unanticipated and totally non-obvious semantic constraints. TypeConverters are ubiquitous and yet most people don't know they exist, so it's pretty easy to make this particular mistake. How can you ever be confident that you're safe from all such presumptions that other code makes about the class? This is one of the biggest problems with inheritance. It is virtually impossible to document fully the set of constraints on an object's behaviour. (The exact same problem surfaces in scenarios other than inheritance by the way. This is why it's so easy for a component upgrade that ostensibly only contains bug fixes to break code that works just fine with the older, supposedly buggier component.) > As far as I can see, if you haven't thought about how a > class would be used for inheritance then you > won't have defined any protected members will you :) > You'll have made all the members public or private. > Derived classes won't be able to access private > members. And you've presumably designed the public > ones so they are safe to use. How can a derived class > change that? You've overlooked overrides of functions that were virtual in the base class of the class you intend to derive from. Of course you could respond to that by saying that override should default to sealed, and that you should explicitly mark methods as virtual if you want derived classes to be able to override them again... (I think that would be reasonable. I've never really thought about this particular aspect before, but I don't think I like C#'s defaults here. And it's arguably inconsistent - interface members use virtual dispatch but are sealed by default...) So supposing we run with a new policy - override *all* virtual methods in the base class, and mark them as sealed. That would eliminate the problem I just showed. So now the question is: what use is inheritance? What can you do with it that you couldn't do without inheritance? Can you show me an example of where it is useful to derive from a class in the .NET framework that doesn't override any methods and doesn't use an protected members? For the record, I don't like the example you gave here: > Even if you have a class with no virtual methods and > nothing that looks useful for inheritance, other people > might still wish to derive from it just for the syntactical > convenience of having a class that implements whatever > your class does plus some other features. One example > of this is System.Drawing.Color. You can't derive from > that because it's a valuetype - but if you could derive > from it I would have done [1]. I'd love to have the > syntactical convenience of having a color type with > some extra methods like Darken(float fractionToDarkenBy) > or some operators to add two colors together (add > RGB components). **Such methods are completely > orthogonal to existing methods** (my emphasis) So what are you doing using inheritance then? Such methods have no business being in the Color class if they are completely orthogonal to everything it does. (The ControlPaint class is the right place for, and where you will find, the nearest equivalent to your Darken method.) The consensus in the C++ community seems long since to be that such methods should actually be global methods. (Global functions seem to have had a bit of a renaissance in C++.) I think this is classic example of what Craig was talking about when he said that inheritance is like a mirage - from a distance it looks like what you want, but turns out to be a disappointing pile of sand close up. You have presented an attractive syntactical shortcut, which makes it look like inheritance would be useful here. But with a little thought I think it becomes clear that inheritance is The Wrong Thing here. There's a very good reason why the two methods you describe should not be members of a derived class (beyond the minor technical nit that they can't). I could very reasonably want to use those on any Color. Why should I be required to obtain your special ColorWithUtils just to use them? What if someone else does the same thing with their own MagicColor class - I now have two mutually incompatible versions of the Color class, and I have to choose which one to use based on an arbitrary split across some utility functions, the split being based on nothing more than who happened to decide to write which utility functions. Such methods clearly belong outside of the class. The usual heuristic that the C++ community (or those C++ mailing lists I still subscribe to) uses is that if a method is capable of doing its job by acting just on the public members of a class, then it should be a global function. It absolutely should not be a member of the class on which it acts. Of course we don't have globals in C#, so we end up with statics instead, which look an awful lot like globals in a namespace... So you end up with methods like ControlPaint.Dark(myColor, myPercentage) which do exactly what your darken method does, but can do it to *any* colour, not just your special derivative. Your argument is essentially one of syntactic convenience. I'll buy that syntactic convenience is potentially a good thing, I just don't agree that inheritance is the right way to go about it. It seems too high a price to pay. And to be honest I've never found myself in a position where I've felt even remotely tempted to use this trick. (So I don't agree that this particular syntactic convenience would be all that useful. Exactly how much typing is this really going to save you?) > It's examples like that that you'd kill by routinely > declaring classes as sealed Good. :-) > when there isn't a good specific reason to. Sounds like reason enough to me... You've convinced me - I'm moving over to sealed as my new default. Together we can stop this madness. ;-) -- Ian Griffiths DevelopMentor You can read messages from the Advanced DOTNET archive, unsubscribe from Advanced DOTNET, or subscribe to other DevelopMentor lists at http://discuss.develop.com.