2010-07-13 70 views
4

也许一个愚蠢的问题,但假设base class A定义virtual method V,是有过一种情况,这将是有意义的derived class C由具有相同签名,宣布了新的virtual method C.VA.V隐藏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"); 
    } 
} 

谢谢你

+0

嗨,正如问题已经显示,这个例子是关于虚拟函数(这是多态性的基础),其中的问题似乎是关于隐藏非虚拟基类成员。这是什么? – 2010-07-13 18:52:35

+1

jdv:在隐藏非虚拟基础成员的这个问题上,我没有看到任何东西。 – dthorpe 2010-07-13 20:06:43

回答

8

隐藏继承虚函数是不是应该做的是故意设计的一部分。语言支持虚拟隐藏功能,使对象框架能够更好地适应未来的变化。

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

后来发布了对象框架X的第2版。 Bob决定将他当前的项目升级为版本2,而不是版本1.对于Bob而言,对象框架X团队也决定使用Print()函数,因此他们添加了一个虚拟Print()框架的基类。

随着虚拟隐藏,包含Bob的打印()实施应该编译并运行良好,即使不同的打印()现在存在于基类Bob的子类 - 即使有不同的方法签名。知道基类Print()的代码将继续使用它,并且知道Bob的Print()的代码将继续使用它。两个人都不会见面。

没有虚隐藏,Bob的代码不会,直到他做一些不平凡的手术,他的源代码,以消除对打印的名称冲突编译在所有的()。有人会认为这是“正确”的事情(拒绝编译),但实际上任何需要修改现有工作代码的基础库的修改都不会与客户一起完成。他们会责怪破坏所有事情的框架,并对此表示不满。

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

+1

根据我的理解,扩展方法对这类事情有更严重的问题。如果Bob创建一个.Print扩展方法,然后基类稍后添加一个工作方式不同的工具,那么一切都会正常工作//直到代码重新编译//。 – supercat 2010-07-13 19:18:40

+1

在代码重新编译之前,一切都如何正常工作?重新编译之前,这些更改尚未应用。 我不记得是否有一个扩展方法,使类中的同名,同名签名方法模糊是C#中的编译器错误。 – dthorpe 2010-07-13 20:04:44

+0

“语言支持虚拟隐藏功能,使对象框架对未来变化更具弹性。”这是C#支持虚拟隐藏的主要原因吗? – flockofcode 2010-07-14 18:43:59

-1

嗯,首先,如果你真的要隐瞒,然后你想要的new关键字。你不想只把virtual放在那里。

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

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

而且,为什么你会怎么做:如果你自己写的类A,我会说你有没有真正的理由。但是,如果你没有,你可能有一个合理的理由,你不希望这种方法叫。在我正在研究的一个项目中,我有一个派生自List<>的类,但我不想要Add调用,我需要调用一个特殊的方法,以便我可以更好地控制添加的项目。在这种情况下,我做了一个new隐藏,并且抛出了一个NotSupportedException,并带有一条消息使用其他方法。

+0

隐藏虚拟方法在语义上与覆盖语义不同,因为从引用基类调用方法时缺少虚拟分发。 – 2010-07-13 18:49:56

1

是否有意义都取决于班级。如果基类功能是您不想让您的课程用户使用的功能,那么您应该隐藏它,以免它们发生任何错误。虽然这通常是在您使用的API没有太多控制权时。如果这是你正在写的东西,最好回去改变方法的访问权限。

举例来说,我使用了一个API,我不控制,它有许多功能暴露,我不希望每个人都使用它。所以在这些情况下,我隐藏了我不想让人们消费的东西。

+0

ewww。如果你打这个电话,那么我可能会争辩说,首先从那个API派生出来的电话很糟糕。希望有一个好的商业理由。 – 2010-07-13 18:52:53

+0

@Greg是的,我同意。在我到达那里之前,这是一个商业决策,他们喜欢这个框架。所以为了避免在阳光下传递一切,我们隐藏了一些东西。 – spinon 2010-07-13 19:14:24

1

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

除了开发者,new也混淆了迷惑工具。所以要警告。

不过,我最近发现一个一个new成员有用的应用程序:我用它来重新声明接口类型ISomthing的只读公共财产为返回的实际类型ConcreteSomething的属性。两者都返回完全相同的对象引用,除非在后一种情况下,对象返回为自身而不是接口指针。它拯救了许多沮丧者。

+0

我只是在想那件事(只读属性成为读写)。在vb.net(pref。2005)中有没有什么方法可以同时创建一个类并覆盖一个函数?例如,如果基类定义了一个抽象只读属性,那么子类是否可以覆盖该只读属性并创建一个具有相同名称的新的可重写读写属性? – supercat 2010-07-13 19:16:06

+0

@supercat:这不是我的意思,但我可以看到它很有用。使用接口,您可以声明只读,并在实现中对其进行读写。如果你使I/F实现明确的'InterfaceName.Property',你会获得更多的自由。 (但要小心:容易过于富有创造力......) – 2010-07-13 19:30:02

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变量引用是没有明智的,因此所有的正常的多态性魔术仍然适用。

相关问题