C#-可以隐藏公共继承的方法(例如,将其私有化为派生类)
假设我有带有公共方法A和B的BaseClass,并且我通过继承创建了DerivedClass。
例如
public DerivedClass : BaseClass {}
现在,我想在使用A和B的DerivedClass中开发方法C。是否有办法可以覆盖方法A和B在DerivedClass中私有,以便仅将方法C公开给想要使用我的DerivedClass的人?
解决方案
如果在原始类中将它们定义为公共,则不能在派生类中将它们重写为私有。但是,我们可以使public方法引发异常并实现自己的私有函数。
编辑:豪尔赫·费雷拉(Jorge Ferreira)是正确的。
我知道的唯一方法是使用Has-A关系,并且仅实现要公开的功能。
@Brian R. Bondy向我指出了一篇有趣的文章,内容涉及通过继承和new关键字进行隐藏。
http://msdn.microsoft.com/zh-CN/library/aa691135(VS.71).aspx
因此,作为解决方法,我建议:
class BaseClass { public void A() { Console.WriteLine("BaseClass.A"); } public void B() { Console.WriteLine("BaseClass.B"); } } class DerivedClass : BaseClass { new public void A() { throw new NotSupportedException(); } new public void B() { throw new NotSupportedException(); } public void C() { base.A(); base.B(); } }
这样的代码将抛出NotSupportedException:
DerivedClass d = new DerivedClass(); d.A();
听起来像个坏主意。利斯科夫不会留下深刻的印象。
如果我们不希望DerivedClass的使用者能够访问方法DeriveClass.A()和DerivedClass.B(),我建议DerivedClass应该实现一些公共接口IWhateverMethodCIsAbout,而DerivedClass的使用者实际上应该与IWhateverMethodCIsAbout并知道完全没有关于BaseClass或者DerivedClass的实现。
不可能,为什么呢?
在C#中,如果继承公共方法,则必须将其公开。否则,他们希望我们首先不要从该类中衍生出来。
不必使用is-a关系,而必须使用has-a关系。
语言设计人员不允许故意这样做,以便我们更正确地使用继承。
例如,一个人可能会不小心混淆了Car类,以从其类Engine派生其功能。但是引擎是汽车使用的功能。因此,我们将需要使用has-a关系。汽车的用户不想访问引擎的界面。汽车本身不应该将引擎的方法与自己的方法混淆。也不是汽车的未来派生。
因此,他们不允许它保护我们免受不良继承层次结构的侵害。
我们应该怎么做呢?
相反,我们应该实现接口。这使我们可以自由使用has-a关系。
其他语言:
在C ++中,我们只需在private,public或者protected的基类之前指定修饰符即可。这使得对该指定访问级别公开的所有基地成员。在我看来,我们无法在C#中执行相同的操作,这似乎很愚蠢。
重组后的代码:
interface I { void C(); } class BaseClass { public void A() { MessageBox.Show("A"); } public void B() { MessageBox.Show("B"); } } class Derived : I { public void C() { b.A(); b.B(); } private BaseClass b; }
我知道上述类的名称有点麻烦:)
其他建议:
其他人建议将A()和B()公开并抛出异常。但这并不能使人们使用友好的课程,也没有任何意义。
隐藏是一个非常湿滑的斜坡。 IMO的主要问题是:
- 它取决于实例的设计时声明类型,这意味着如果我们执行类似BaseClass obj = new SubClass()的操作,然后调用obj.A(),则隐藏将失败。 BaseClass.A()将被执行。
- 隐藏很容易掩盖基本类型中的行为(或者行为更改)。当我们拥有等式的两边时,或者如果调用" base.xxx"属于子成员的一部分,则显然不必担心此问题。
- 如果我们实际上确实拥有基类/子类方程式的两边,那么我们应该能够设计出比制度化的隐藏/阴影更易于管理的解决方案。
我想说的是,如果我们有想要使用的代码库,那么它并不是最佳设计的代码库。通常,这是层次结构中一个级别上需要某个公共签名的类的标志,而从该类派生的另一个类则不需要它。
即将出现的编码范例称为"继承之上的组合"。这直接脱离了面向对象开发的原则(尤其是单一责任原则和开放/封闭原则)。
不幸的是,在我们许多开发人员学习面向对象的方法中,我们养成了立即考虑继承而不是组合的习惯。我们倾向于拥有承担许多不同职责的较大类,这仅仅是因为它们可能包含在同一"真实世界"对象中。这可能会导致5层以上级别的类层次结构。
开发人员在处理继承时通常不会想到的一个不幸的副作用是,继承形成了我们可以引入到代码中的最强大的依赖形式之一。现在,派生类非常依赖于其继承的类。从长远来看,这可能会使代码更加脆弱,并导致令人困惑的问题,即在基类中更改某些行为会以晦涩的方式破坏派生类。
分解代码的一种方法是通过另一个答案中提到的接口。无论如何,这都是一件很明智的事情,因为我们希望类的外部依赖关系绑定到抽象而不是具体/派生的类型。这样,我们就可以在不更改接口的情况下更改实现,而又不会影响从属类中的代码行。
我宁愿维护一个拥有成百上千个甚至更小的类且松散耦合的类的系统,而不是处理一个大量使用多态性/继承性且更少类紧密耦合的系统。
关于面向对象开发的最佳资源也许是Robert C. Martin的书《敏捷软件开发,原理,模式和实践》。
我们需要的是组成而不是继承。
class Plane { public Fly() { .. } public string GetPilot() {...} }
现在,如果我们需要一种特殊的飞机,例如具有PairOfWings = 2的飞机,但否则飞机会做的所有事情。我们可以继承飞机。这样,我们可以声明派生符合基类的约定,并且可以在不期望基类出现的任何位置被替换而不会闪烁。例如LogFlight(Plane)将继续与BiPlane实例一起使用。
但是,如果我们只需要为要创建的新Bird设置Fly行为,并且不愿意支持完整的基类合同,则可以改成撰写。在这种情况下,重构方法的行为以重用到新的Flight中。现在,在Plane和Bird中创建并保存对该类的引用。
我们不会继承,因为Bird不支持完整的基类协定...(例如,它不能提供GetPilot())。
出于相同的原因,当我们重写时,我们不能降低基类方法的可见性。我们可以在派生中重写并使基类私有方法公开,但反之则不行。例如在此示例中,如果我派生一种平面" BadPlane",然后重写和"隐藏" GetPilot(),则将其设为私有;客户端方法LogFlight(Plane p)适用于大多数Planes,但是如果LogFlight的实现恰好需要/调用GetPilot(),则该方法对于" BadPlane"将不起作用。由于在任何期望有基类参数的地方,所有基类的派生都可以被"替代",因此必须禁止这样做。
例如,当我们尝试从List <object>继承时,我们想隐藏直接的Add(object _ob)成员:
// the only way to hide [Obsolete("This is not supported in this class.", true)] public new void Add(object _ob) { throw NotImplementedException("Don't use!!"); }
实际上,这并不是最可取的解决方案,但可以完成工作。 Intellisense仍然接受,但是在编译时会出现错误:
error CS0619: 'TestConsole.TestClass.Add(TestConsole.TestObject)' is obsolete: 'This is not supported in this class.'