简体   繁体   中英

Is there ever a situation where derived class should hide …?

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 AV by declaring a new virtual method CV with the same signature as AV :

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

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.

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.

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM