是否存在派生类应隐藏的情况?

时间:2010-07-13 18:35:36

标签: c# polymorphism

也许是一个愚蠢的问题,但假设base class A定义了virtual method V,是否有一种情况可能会让derived class C通过声明A.V来隐藏virtual method C.VA.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"); } }

{{1}}

谢谢

5 个答案:

答案 0 :(得分:8)

隐藏继承的虚拟不应该作为故意设计的一部分来完成。语言支持虚拟隐藏,使对象框架对未来的变化更具弹性。

示例:对象框架X的第1版不提供Print()函数。 Bob决定通过在他自己的后代类中定义Print()函数来扩展一些框架X对象。由于他计划在更具体的类中覆盖它们,因此他也使Print()函数成为虚拟的。

稍后,发布了对象框架X的第2版。 Bob决定升级他当前的项目以使用Release 2而不是Release 1.对于Bob来说,对象框架X团队还决定Print()将是一个有用的功能,因此他们在其中一个中添加了一个虚拟的Print()。框架的基类。

使用虚拟隐藏,Bob的后代类包含Bob的Print()实现应该编译并运行正常,即使基类中现在存在不同的Print() - 即使使用不同的方法签名。知道基类Print()的代码将继续使用它,知道Bob的Print()的代码将继续使用它。这两个人永远不会见面。

没有虚拟隐藏,Bob的代码根本不会编译,直到他对他的源代码进行一些非平凡的手术以消除Print()上的名称冲突。有人会认为这是“正确”的事情(拒绝编译),但实际上,任何需要修改现有工作代码的基础库的修改都不会与客户完全一致。他们会责怪破坏一切的框架并且说不好。

鲍勃得到关于基本打印被Bob的打印模糊的编译器警告是合理的,但这不是致命的错误。 Bob应尽快清理(通过重命名或删除他的Print()函数)以避免人为混淆。

答案 1 :(得分:2)

我已经看到它在你希望自动将基类的成员从子类强制转换为更窄类型的场景中使用。

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; }
    } 
}

在这种情况下,对ConcreteBar的引用可以在没有显式强制转换的情况下提取对ConcreteFoo的引用,同时BarIFoo变量引用都不是更聪明,所以他们所有正常的多态性魔法仍然适用。

答案 2 :(得分:1)

是否有意义取决于课程。如果基类功能是您不希望类用户使用的,那么您应该隐藏它以便它们不会出现任何错误。虽然这通常是在您使用API​​时,您没有太多的控制权。如果您正在编写它,那么最好还是返回并更改方法的访问权限。

例如,我使用了一个我无法控制的API,它暴露了很多功能,我不希望每个人都使用它。所以在那些情况下,我隐藏了我不想让人们消费的东西。

答案 3 :(得分:1)

加入合唱,我同意这是一个坏主意。我见过的大多数例子都是开发人员喜欢基类方法是虚拟的,但遗憾的是它不是。然后将其声明为new并希望没有人通过指向对象实例的基类指针调用此方法。 能够采用virtual方法并将其重新声明为new virtual,这是达尔文奖项类别。

除了开发人员之外,new还会混淆混淆工具。所以请注意。

尽管如此,我最近为new成员找到了一个有用的应用程序:我用它来重新声明接口类型ISomthing的只读公共属性作为返回的属性实际类型ConcreteSomething。两者都返回完全相同的对象引用,除了在后一种情况下,对象作为自身而不是接口指针返回。它挽救了许多垂头丧气者。

答案 4 :(得分:-1)

首先,如果你真的想要隐藏,那么你需要new关键字。您不想再将virtual放在那里。

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

但是因为这个方法是虚拟的,所以你并没有真正隐藏任何东西。您的派生类应该覆盖该方法并赋予它独特的行为。但是,如果该方法不是虚拟的,但您确实需要更改它,那么您可以使用new关键字。请注意,您无法通过将方法重写为私有来完全隐藏方法。如果您尝试,它将只调用基类中的公共实现。您可以做的最好是覆盖它以抛出NotSupportedException

至于你为什么要这样做:如果你自己写A级,我会说你没有真正的理由。但如果你没有,你可能有一个正确的理由,你不希望调用该方法。在我正在研究的一个项目中,我有一个派生自List<>的类,但我不想调用Add,我要求调用一个特殊的方法,以便我可以更好地控制那些得到补充。在那种情况下,我进行了new隐藏,并使其抛出NotSupportedException,并使用另一种方法发送消息。