views:

398

answers:

6

Possible Duplicate:
Why C# implements methods as non-virtual by default?

I'm speaking primarily about C#, .NET 3.5, but wonder in general what the benefits are of not considering everything "virtual" - which is to say that a method called in an instance of a child class always executes the child-most version of that method. In C#, this is not the case if the parent method is not labeled with the "virtual" modifier. Example:

public class Parent
{
    public void NonVirtual() { Console.WriteLine("Non-Virtual Parent"); }
    public virtual void Virtual(){ Console.WriteLine("Virtual Parent"); }
}

public class Child : Parent
{
    public new void NonVirtual() { Console.WriteLine("Non-Virtual Child"); }
    public override void Virtual() { Console.WriteLine("Virtual Child"); }
}

public class Program
{
    public static void Main(string[] args)
    {
        Child child = new Child();
        Parent parent = new Child();
        var anon = new Child();

        child.NonVirtual();           // => Child
        parent.NonVirtual();          // => Parent
        anon.NonVirtual();            // => Child
        ((Parent)child).NonVirtual(); // => Parent

        child.Virtual();              // => Child
        parent.Virtual();             // => Child
        anon.Virtual();               // => Child
        ((Parent)child).Virtual();    // => Child
    }
}

What exactly are the benefits of the non-virtual behavior observed above? The only thing I could think of was "What if the author of Parent doesn't want his method to be virtual?" but then I realized I couldn't think of a good use case for that. One might argue that the behavior of the class is dependent on how a non-virtual method operates - but then that seems to me there is some poor encapsulation going on, or that the method should be sealed.

Along these same lines, it seems like 'hiding' is normally a bad idea. After all, if a Child object and methods were created, it seems that it was done so for a specific reason to override the Parent. And if Child implements (and hides the parents) NonVirtual(), it is super easy to not get the what many might consider "expected" behavior of calling Child::NonVirtual(). (I say "expected" because it is sometimes easy to not notice 'hiding' is happening).

So, what are the benefits of not allowing everything to have "virtual" behavior? What is a good use-case for hiding a non-virtual parent if it's so easy to get unexpected behavior?

If anyone is curious as to why I pose this question - I was recently examining Castle Projects DynamicProxy library. The one main hurdle in using it is that any method (or property) you want to proxy has to be virtual. And this isn't always an option for developers (if we don't have control over the source). Not to mention the purpose of DynamicProxy is to avoid-coupling between your proxied class and whatever behavior you are trying to achieve with the proxy (such as Logging, or perhaps a Memoization implementation). And by forcing virtual methods to accomplish this what is instead achieved is very thin but obtuse coupling of DynamicProxy to all the classes it is proxying - Imagine, you have a ton of methods labeled virtual even though they are never inherited and overridden, so any other developer looking at the code might wonder "why are these even virtual? lets change them back".

Anyway, the frustration there led me to wonder what the benefits are of non-virtual, when it seems having everything virtual might have been more clear (IMO, I suppose) and perhaps(?) have more benefits.

EDIT: Labeling as community wiki, since it seems like a question that might have subjective answers

A: 

One key benefit of a non-virtual method is that it can be bound at compile time. That is the compiler can be sure which actual method is to be called when a method is used in code.

The actual method to be called cannot be known at compile time if that method is declared virtual, since the reference may actually point to a sub-type that has overriden it. Hence there is a small overhead at runtime when the actual method to call needs be resolved.

AnthonyWJones
But with all the arguments that "premature optimization is the root of all evil" should we care if it takes an extra instruction or two? (How much faster is compile time binding than runtime?)
Matt
I realized after I said that that "an instruction or two" across the entire runtime might add up. But I am curious - how much faster is it?
Matt
Actually, that isn't true. The C# compiler uses callvirt even for non-virtual instance methods.
Marc Gravell
If I remember correctly, the compiler emits a callvirt instruction even if the method is not virtual, so there is actually no performance benefit...
Thomas Levesque
Just because the compiler emits callvirt (for null checking purposes) doesn't mean the CLR can't optimise non-virtual method calls. I'm not saying it can either - just that the IL isn't the important bit.
Jon Skeet
+4  A: 

Eric Lippert on method hiding

Marc Gravell
Good read, thanks. His GST / Grocery example was very informative.
Matt
+1  A: 

In many cases, it is crucial for a class to function properly that a given method has a specific behavior. If the method is overridden in an inherited class, there is no guarantee that the method will correctly implement the expected behavior. You should only mark a method virtual if your class is specifically designed for inheritance and will support a method with a different implementation. Designing for inheritance is not easy, there are many cases where incorrectly overriding a method will break the class's internal behavior

Thomas Levesque
A: 

In a framework, a non-virtual member could be called and have a range of expected outputs, if the method was virtual the result of the method could be an expected result that wasn't tested for. Allowing methods to be non-virtual give expected results to framework actions.

Jeff Martin
A: 

Simple: The entire point in a class is to encapsulate some kind of abstraction. For example, we want an object that behaves as a text string.

Now, if everything had been virtual, I would be able to do this:

class MessedUpString : String{
   override void Trim() { throw new Exception(); }
}

and then pass this to some function that expects a string. And the moment they try to trim that string, it explodes.

The string no longer behaves as a string. How is that ever a good thing?

If everything is made virtual, you're going to have a hard time enforcing class invariants. You allow the class abstraction to be broken.

By default, a class should encapsulate the rules and behaviors that it is expected to follow. Everything you make virtual is in principle an extensibility hook, the function can be changed to do anything whatsoever. That only makes sense in a few cases, when we have behavior that is actually user-defined.

The reason classes are useful is that they allow us to ignore the implementation details. We can simply say "this is a string object, I know it is going to behave as a string. I know it will never violate any of these guarantees". If that guarantee can not be maintained, the class is useless. You might as well just make all data members public and move the member methods outside the class.

Do you know the Liskov Substitution Principle? Anywhere an object of base class B is expected, you should be able to pass an object of derived class D. That is one of the most fundamental rules of object-oriented programming. We need to know that derived classes will still work when we upcast them to the base class and pass them to a function that expect the base class. That means we have to make some behavior fixed and unchangeable.

jalf
Maybe the `MessedUpString` doesn't support Trimming? But `String` is sealed, so I believe it would be an exception in that imaginary world anyway.
Groo
If we had followed the advice of "everything should be virtual", then it wouldn't be sealed so it could be done. And the point here is that if it is derived from String, and String supports trimming, then the derived class *must also support trimming*. Otherwise it violates the LSP, and is a pain to work with. You must have run into the same problem in other situations. Say .NET's streams, which may or may not support seeking, so a function has no way to indicate that it expects "a stream that supports seeking", and is instead forced to throw unexpected exceptions at runtime
jalf
+5  A: 

Because you don't want people overriding methods that you haven't designed the class for. It takes a significant effort to make sure it is safe to override a method or even derive from a class. It's much safer to make it non-virtual if you haven't considered what might happen.

Zifre