Share via


More on virtual by default

Gary and James took issue with my position, so I'd like to expand a bit on what I said.

Gary wrote:

Eric suggests that final should be used in most cases with virtual reserved for the special occasions. I totally disagree, you can't lay down the law as to how another developer is going to extend your class, (other than in the special circumstances I mention here ). If the choice is being flexible versus being static, then in the interests of good software engineering you *must* go with the flexible option. Or is that just the Smalltalk programmer in me coming out :)

My answer to that question would be “yes”. I'm not being flippant on that, and I'm about as far from being a Smalltalk programmer as you can be, but I think it comes down to the tradeoff between robustness and flexibility. If you allow more extensibility, you are being more flexible, and also being less robust (or, to be precise, being potentially less robust), especially if you aren't testing the extensibility.

James wrote:

The long and short of it - Eric thinks that library designers know everything and that one of the primary jobs is to protect those dumb application developers. There's just no telling what they might do if we let them.

James has it backwards. I don't think that library designers know everything. I think that they know very little about how users might use their classes, and therefore shouldn't be making promises their code can't keep. If they want to support extensibility, then that should be something they design well and test for. If they aren't going to go to that effort, than I don't think providing extensibility that will “probably work” is doing their users a service, and it may be actively doing them a disservice. Not only will such code be more likely to fail, but the constraint of such virtuals limits how the class can evolve in the future. It's bad when you'd like to make a small change that would help 99.9% of your users, but you can't because somebody might have existing code that depends on your behavior. But maybe that's just the compiler guy in me...

I do think that this depends on the kind of library you're building. In general, the more closely coupled the api writer and user ae, theless of an issue this is.

Comments

  • Anonymous
    May 18, 2004
    It also matters if the source code for the library is available. For libraries like the .NET framework, being completly virtual does not make a whole lot of sense to me at least, simply because it is difficult to understand all the conditions that a given function may be called under (or even the precise semantics that a given function is required to implement).
  • Anonymous
    May 18, 2004
    virtual by default is absurd - it's just a game of russian roulette! It's like said above, it's not a question of knowing what people are going to do with your class - it's more the fact that BECAUSE YOU DON'T KNOW WHAT THEY ARE GOING TO DO - YOU HAVE TO BE CAREFUL.

    easy example -
    public void Open {_open=true;_pointer = OpenTheFile();}
    public void Close {if (_open) CloseFile(m_pointer);}

    If some idiot starts overriding open, and doesn't properly set m_open or something, then
    the close will fail badly. Because there is no way of guaranteeing that anyone who override calls the base class, the ONLY safe way of doing the above class is by making them final... and if you do want them sort of overridable, then you make them protected final functions, and make virtual stub functions that call them..

    Especially if you don't give out your source, I put it to you that the % of functions that it would be REALLY REALLY BAD if someone overrode them and didn't properly do what those functions did is much much higher than the % of functions that you can safely override!

    (Quite apart from the fact that you want proper encapsulation - I NEVER let any of my coders create a protected class variable - you can make a protected property accessor, but ALL member variables are always private - so it would be impossible to successfully duplicate the functionality of the base class in a child class, without just calling base->function)
  • Anonymous
    May 18, 2004
    The comment has been removed
  • Anonymous
    May 18, 2004
    The comment has been removed
  • Anonymous
    May 18, 2004
    Now that you mention it, I agree with robustness over flexibility because flexibility is often a mirage.

    Take the HttpWebRequest class for instance. I wanted to override a method that is virtual, but looking at the underlying code of the method, it calls upon methods and objects that are declared private and need to be manipulated. Since I wouldn't have access to these classes, It's darn near impossible for me to completely override this method. This is a case where the method perhaps would be better off final, or at least with documentation describing guidelines for overriding it.
  • Anonymous
    May 18, 2004
    The comment has been removed
  • Anonymous
    May 18, 2004
    I've done my fair share of cursing about sealed classes and final methods, but it is mostly about the specific decisions made to seal/finalize those things.

    I like "Mom's Rules", I just don't think that the asp:listitem is sharp and pointy.

  • Anonymous
    May 18, 2004
    I'm sick of Microsoft treating everybody like an idiot. If you don't know how to code then you shouldn't be coding. Apparently Microsoft doesn't want knowledgable people to use their products, because they're doing everything they can to discourage them while making their products so an idiot (and only an idiot) can use them.
  • Anonymous
    May 18, 2004
    Hey my original post wasn't designed to be Microsoft bashing, in fact I think that - in general - the .Net framework and VS.Net is one of the best things they've done. I don't think the idea of sealed classes and final is a Microsoft thing either per se.

    All I'm saying is that I feel that a design that seeks to constrain users, simply because a minority of them may get in trouble through their own bad practices, is not a good one.

    If I create a class I can't possibly imagine all the creative uses that the user community will put it to by extending it, therefore I should only prevent them from doing stuff I know will be dangerous and not prevent them from doing things that might be dangerous if they get it wrong. I mean, it's dangerous to drive and blog at the same time, but my laptop still boots up when I'm in the car - it's down to me to do the sensible thing :)
  • Anonymous
    May 18, 2004
    Gary,

    I'm sorry if people got that impression - I explicitly included the link that you talked about because I thought it made it more clear what your position was.

    Eric
  • Anonymous
    May 18, 2004
    I think the best thing I've seen on this issue is "Versioning, Virtual, and Override
    A Conversation with Anders Hejlsberg, Part IV
    " (http://www.artima.com/intv/nonvirtual.html).

    I read that and thought "Ahhhhhhhhhhhh, that's why they do it."
    This is easily one of the more profound differences between C# and Java.
    I was particularly struck by the point about how if everything is virtual by default, then adding a method to the base class breaks any derived class that has a method of the same name and return type. Where's flexibility then?
  • Anonymous
    May 18, 2004
    I think someone proposed a rule something like this:
    "you may not use sealed classes as paramaters or return values in public methods"

    Instead have the sealed class implement an interface and declare interfaces as the paramaters and/or return values.
  • Anonymous
    May 18, 2004
    Some programmers did the most amazing things with the Commodore 64, things which the designers thought where impossible. If they defined what they didn't know as impossible or not needed and sealed down the whole cbm64, how fun or useful would that have been?

    The Open/Close example given is an excelent example of poor design. It is obvious that these methods are not suitable for overriding, hence they should be sealed. A good designer would have introduced templated method or event.

    I guess we are both right and wrong. Can't we meet on middle ground and remove the virtual keyword? An 'override' should be enought, a missing 'override' should mean 'new'. Having to declare both virtual and override is meaningless.

    You can't override something which doesn't exist and a missing override will not, so introducing a new base method with the same name as a derived class method should not break anything. It would keep todays functionallity with simpler syntax.
  • Anonymous
    May 18, 2004
    The comment has been removed
  • Anonymous
    May 18, 2004
    Kevin Daly- that's actually a good piece for why features like override are nice but most of the discussion on virtual vs. non-virtual is off-track

    > Where's flexibility then?

    In the derived class. The base class never has flexibility in this case because adding new methods is an incompatible change. Even adding new non-virtual methods can break clients (the problem derives from accessibility during method resolution not inheritance).
  • Anonymous
    May 18, 2004
    The comment has been removed
  • Anonymous
    May 18, 2004
    The comment has been removed
  • Anonymous
    May 18, 2004
    An interesting observation is that a lot of people are arguing virtual by default or non-virtual by default but Eric is the only person I've seen in either thread say that the intended user matters when making that decision.
  • Anonymous
    May 18, 2004
    The mistake is in thinking - as the library designer - that you have any idea whatsoever who the "intended user" is. You don't.
  • Anonymous
    May 18, 2004
    James:
    One knows exactly who the intended user is, it is someone who uses it in the way it was intended to be used.

    The problem is that one does not often know who the actual user is.

  • Anonymous
    May 18, 2004
    If I declare a base method to be virtual, will it still be safe down the inheritance chain? The arguments for not having virtual as default should apply here as well. You are changing the behaviour of a method when you override it, so the author of the overrided class has no longer control. The original intent may be gone and the virtual is no longer safe.

    So the safer thing to do would be to demand the virtual modifier again if further overriding is wanted. We would then have code like:

    public virtual override void Something()

    If you think this is stupid, why do you think no virtual by default is not?
  • Anonymous
    May 18, 2004
    My take at http://blogs.geekdojo.net/pdbartlett/archive/2004/05/19/VirtualByDefault.aspx

    I should point out that the URL might give the wrong impression - my stance is pretty much "pro choice", to use a Roryism.
  • Anonymous
    May 18, 2004
    Now factor in change in the library over time. If a method that wasn't explicitly intended to be overridden is overridden (i.e. default virtual, and a user decides to override it), and the implementation of the base method is changed in some way, again expecting that it hasn't been overridden, derived classes could be broken by this change.

    Strongly-named assemblies/side-by-side can help, but if a publisher policy is used to push a different version of the assembly, what then? You have to use <bindingRedirect>s to keep using the old version which worked.

    It's a lot easier to change a method from final to virtual than the other way around. The first is an almost invisible change; the second is potentially a breaking change.

    On the whole it's safer only to make the methods you actually want to be extensibility points virtual.
  • Anonymous
    May 18, 2004
    "On the whole it's safer only to make the methods you actually want to be extensibility points virtual."

    The problem is that the methods I want to make virtual, or think I want to, are not the same ones as you want. There lies the problem.

    I am not a phsycic, I can't predict the future. Hence virtual by default is better.

    The problem is not the method being virtual, but if someone has overridden it. No potential harm is done if the method is not overridden. You can't override by default, but have to use the override modifyer. Isn't that enough?

    It is also very possible to change a virtual method in a breaking way.
  • Anonymous
    May 18, 2004
    The comment has been removed
  • Anonymous
    May 18, 2004
    I am rolling on the floor laughing. It's funny to see TDD as an argument against virtual by default. Because of TDD you can have virtual by default and still be safe. With TDD you can remove all safety nets and still be safe. That said, I still don't believe sealed/final by default is a safety net. Not with TDD.
  • Anonymous
    May 19, 2004
    The comment has been removed
  • Anonymous
    May 19, 2004
    Darren - so what? If somebody decides to break code then let them do it. It is their choice. If you can't override functions and do it safely then just don't do it but don't force your opinions about what should be allowed or not on the rest of us.
  • Anonymous
    May 19, 2004
    I can think of reasons why a developer might not want to release code that can be easily extended - i.e. not wanting users to become reliant on features or behaviors that might change under them in a later release.

    I also tend to think that the code that comes out of Microsoft, especially the base class library, should be built for use, reuse, extension, modification etc. Given the degree of fan-out from microsoft developers to us, it seems criminal to force people to re-implement code rather than extending of modifying it.

    If more extensible code means less code, so be it. Anyone who has worked with the BCL for more than 6 months has cursed Microsoft at some time for their sealed, private, internal or otherwise inaccessible code, I know I have.

    Clearly there are limits; but it must be possible to write a library for us that has fewer limits on it.
  • Anonymous
    May 19, 2004
    That's why they say that you should only inherit from abstract classes. If you plan your class to be extensible allowing the user reimplementing one or more methods, then make an abstract base class with those methods being abstract.

    If the user of your library decides to override your non abstract method, then he takes the risk of having his code break later, by a change in your code. OK, then you might ask then what's the point in having all our methods virtual... :) It's a question of directing and enabling menthality (see www.martinfowler.com), the same as with the static and dynamic/latent typing. (And the enabling approach is far from being disasterous.)
  • Anonymous
    May 20, 2004
    Someone else having their code break is your problem how, exactly?

    Don't worry about protecting people from themselves; all you end up doing is creating extra work for them.
  • Anonymous
    May 20, 2004
    I see two discussions here: One is about virtual by default, the other is when/not it is safe/wanted to override/inherit something.

    It seems to me that it is not virtual by default which is harmful, but override by default. C# has neither, so there is a double protection which is definitly not necessary.

    We could have virtual by default, because we don't have override by default. And you can't override something which is not there.

    A compromise would be to give us the virtual class, which someone suggested. The virtual class would have all members virtual by default. In this way the decision is back where it belongs.
  • Anonymous
    May 20, 2004
    I think alot of people have missed the hidden nugget in the original post here. GoVirtual, for example, complains 'I don't need MSFT running around the code libraries locking everything down "for my benefit."' Similar thoughts are expressed all through the comments. While I agree 110% with that sentiment, I think they're missing the evil-m$-self-interestTM. The point is that microsoft is avoiding having you override their implementations so they can go back and fix bugs without breaking your code. They're not trying to prevent you from hurting yourself - think Outlook .pif attachments - they're trying to prevent you from hurting THEM! Dudeman says so in the post itself:
    "It's bad when you'd like to make a small change that would help 99.9% of your users, but you can't because somebody might have existing code that depends on your behavior."
    It's hard to disagree with that. Just remember: they're only locking things down 'for your benefit' in the twisted sense of "what's good for microsoft is good for you."
  • Anonymous
    May 20, 2004
    I think there are two perspectives here. On the one hand, we have people used to writing code in tightly-coupled environments. They wrote the base class, they wrote the derived classes, they know the invariants, and the whole thing gets compiled at once. On the other hand, we have Eric and a few others who grok that security and maintainability in the face of runtime linking, reflection, and code access security requires testing any extensiblity scenario that you expose from a reusable library.

    Separate from this, is a few people complaining about a few instances in the FCL where the reasons why a class or method has to be sealed are non-obvious and therefore annoying to people who don't investigate the reasons. Additionally there are a few cases where Microsoft has admitted that unsealing a class or method would work and would be a good idea, but they didn't have enough time to test it before releasing the code. I think people should show more understanding for the fact that even Microsoft has limited resources, and that if its a choice between security/versionability/robustness and some minor speculative feature, security should win every day. You can almost always work around these issues with delegation anyway (e.g., the StreamReader example given above by James).
  • Anonymous
    May 20, 2004
    Doug, you explain the issue very well. By now it is clear that we have two parties with non-compatible interests.

    1. The library manufacturer who needs to fix bugs without breaking existing clients,

    and

    2. Application developers like me who has full control over the code written.

    I see the default settings favours library coders, but not application coders. I think it's safe to assume there are more application coders than library coders. Shouldn't the language/tool favour both?

    The defaults doesn't hurt Microsoft, because they need them as they are. They will never feel our pain, but continue to argue why the defaults has to be what they are.

    I see possible solutions, but don't expect to see any of them implemented:

    1. Loosen up the defaults and provide a tool to lock down everything implicit which is not used. That is, all methods are virtual by default, but the tool make all non-overridden methods final if they are not explicitly marked virtual.

    2. Get rid of the silly double protection. Why should I have to first specify virtual, then override?

    3. Leave things as is, but let the tool automaticly change the overridden method to virtual. After all, if I override a method, it is by intention. If the method is not virtual, I would change it manually. Why not let the tool do it?

    I can accept inflexible assemblies, but not inflexible source code.