views:

124

answers:

5

Perhaps a stupid question, but assuming base class A defines a virtual method V, is there ever a situation where it would make sense for a derived class C to hide A.V by declaring a new virtual method C.V with the same signature as A.V:

class Program
{
    static void Main(string[] args)
    {
        A a = new C();
        a.Print(); // prints "this is in B class"

        C c = new C();
        c.Print();// prints "this is in C class"
    }
}

class A
{
    public virtual void Print()
    {
        Console.WriteLine("this is in A class");
    }
}

class B:A
{
    public override void Print()
    {
        Console.WriteLine("this is in B class");
    }
}

class C : B
{
    public virtual void Print()
    {
        Console.WriteLine("this is in C class");
    }
}

Thank you

A: 

Well, first of all, if you really mean to hide, then you want the new keyword. You don't want to just put virtual in there again.

class C : B
{
    public new void Print()
    {
        Console.WriteLine("this is in C class");
    }
}

But since the method was virtual to begin with, you aren't really hiding anything. Your derived classes are expected to override the method and give it unique behavior. However, if the method wasn't made virtual, but you really need to change it, then you can use the new keyword. Note that you cannot completely hide a method by overriding it to be private. If you try, it will just call the public implementation in the base class. The best you can do is override it to throw a NotSupportedException.

And as to why you would do this: if you wrote class A yourself, I would say you have no real reason. But if you didn't, you might have a valid reason that you don't want that method called. In one project I'm working on, I have a class derived from List<>, but I don't want Add called, I require that a special method is called so that I can better control items that get added. In that case I did a new hiding and made it throw a NotSupportedException, with a message to use the other method.

Tesserex
Hiding a virtual method isn't semantically identical to overriding due to the lack of virtual dispatch when the method is called from a reference to the base class.
Greg D
+8  A: 

Hiding inherited virtuals is not something that should be done as part of a deliberate design. Languages support virtual hiding to make object frameworks more resilient to future change.

Example: Release 1 of object framework X does not provide a Print() function. Bob decides to extend a few of the framework X objects by defining Print() functions in descendant classes of his own. Since he plans to override them in more specific classes, he also makes the Print() function virtual.

Later, Release 2 of object framework X is released. Bob decides to upgrade his current project to use Release 2 instead of Release 1. Unbeknownst to Bob, the object framework X team also decided Print() would be a useful function to have and so they added a virtual Print() in one of the base classes of the framework.

With virtual hiding, Bob's descendant classes containing Bob's Print() implementation should compile and run fine even though a different Print() now exists in the base classes - even with a different method signature. The code that knows about the base class Print() will continue to use it, and the code that knows about Bob's Print() will continue to use it. Never the two shall meet.

Without virtual hiding, Bob's code will not compile at all until he does some non-trivial surgery to his source code to eliminate the name conflict on Print(). Some would argue this is the "correct" thing to do (refuse to compile) but realistically any rev of a base library that requires revising existing working code will not go over well with customers. They will blame the framework for breaking everything and speak ill of it.

It would be reasonable for Bob to get a compiler warning about the base Print being obscured by Bob's print, but this isn't a fatal error. It's something that Bob should probably clean up (by renaming or eliminating his Print() function) as soon as possible to avoid human confusion.

dthorpe
By my understanding, extension methods have a worse problem with this sort of thing. If Bob creates a .Print extension method, and then the base class later adds one which works differently, everything will work fine //until the code is recompiled//.
supercat
How does everything work fine before the code is recompiled? The changes haven't been applied until it's recompiled.I don't recall offhand whether having an extension method that obscures a same-named, same-signature method in the class is a compiler error in C#.
dthorpe
"Languages support virtual hiding to make object frameworks more resilient to future change." Is this the main reason for C# supporting virtual hiding?
flockofcode
@flockofcode: Yes. It was the reason virtual hiding was implemented in the Delphi language in the mid 90's. I wasn't part of the design of C#, but I'd say it would be a reasonable assumption that C# supports virtual hiding for the same reasons that Delphi does. C# and Delphi languages were designed by the same architect, after all - Anders Hejlsberg.
dthorpe
thank you all for your help
flockofcode
+1  A: 

Whether it makes sense all depends on the class. If the base class functionality is something that you don't want available to your class users then you should hide it so that they don't make any mistakes with it. Though this is usually when you are consuming an API that you don't have too much control over. If it is something that you are writing it's probably better to just go back and change the access for the methods.

For instance I use an API, that I don't control, that has a lot of functionality exposed and I don't want everyone using it. So in those instances I hide what I don't want people consuming.

spinon
ewww. If you're making that call, then I might argue that deriving from that API in the first place was a bad call. Hope there's a good business reason for it.
Greg D
@Greg Yeah I agree. It was a business decision before I got there and they loved the framework. So to avoid passing everything under the sun down we hid stuff.
spinon
+1  A: 

To join the chorus, I agree it's a bad idea. Most examples I've seen are that the developer would have liked a base class method to be virtual, but unfortunately it wasnt. Then you declare it as new and hope that nobody is calling this method via a base class pointer to your object instance. The ability to take a virtual method and redeclare it as new virtual, that's a Darwin award category.

Beside developers, new also confuses obfuscation tools. So be warned.

Nevertheless, I recently found one useful application for a new member: I used it to redeclare a readonly public property of interface type ISomthing as a property that returned the actual type ConcreteSomething. Both returned exactly the same object reference, except in the latter case the object is returned as itself rather than an interface pointer. It saved many many downcasts.

jdv
@jdv: I was just thinking about that very thing (read-only property becoming read-write). Is there any way in vb.net (pref. 2005) to have a class simultaneously shadow and override a function? For example, if a base class defines an abstract read-only property, can a subclass both override that read-only property and create a new overridable read-write property of the same name?
supercat
@supercat: that's not what I meant, although I can see that it's useful. With interfaces you can declare read-only, and make it read-write in the implementation. If you make the i/f implemention explicit `InterfaceName.Property` , you gain even more freedom. (but watch out: its easy to get too creative...)
jdv
+2  A: 

I have seen it used in scenarios where you want to automatically cast a member of a base class to narrower type from a subclass.

public interface IFoo { }

public class ConcreteFoo : IFoo { }

public abstract class Bar
{
    private IFoo m_Foo;

    public IFoo Foo
    {
        get { return m_Foo; }
    }

    protected void SetFoo(IFoo foo)
    {
        m_Foo = foo;
    }
}

public class ConcreteBar : Bar
{
    public ConcreteA(ConcreteFoo foo)
    {
        SetFoo(foo);
    }

    public new ConcreteFoo Foo
    {
        get { return (ConcreteFoo)base.Foo; }
    } 
}

In this scenario a reference to ConcreteBar can extract a reference to ConcreteFoo without the explicit cast while at the same time the Bar and IFoo variable references are none the wiser so all of their normal polymorphism magic still applies.

Brian Gideon