假设我有公共方法A和B的BaseClass,我通过继承创建DerivedClass。
e.g。
public DerivedClass : BaseClass {}
现在我想在DerivedClass中开发一个使用A和B的方法C.有没有办法可以在DerivedClass中将方法A和B重写为私有,这样只有方法C才会暴露给想要使用我的DerivedClass的人?
答案 0 :(得分:68)
这不可能,为什么?
在C#中,强制要求你继承公共方法,你必须公开它们。否则他们希望你不要从这个阶段派生出来。
您必须使用has-a关系,而不是使用is-a关系。
语言设计者不会故意允许这样做,以便您更正确地使用继承。
例如,有人可能会意外地将类Car混淆为从类Engine派生以获得它的功能。但引擎是汽车使用的功能。所以你想要使用has-a关系。 Car的用户不希望访问Engine的界面。汽车本身不应该将引擎的方法与它自己的方法混淆。 Nor Car的未来派生。
所以他们不允许它保护你免受糟糕的继承层次结构的影响。
你应该做什么呢?
相反,你应该实现接口。这使您可以使用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()公开并抛出异常。但是,这并没有为人们提供友好的课程,而且实际上没有意义。
答案 1 :(得分:24)
例如,当您尝试从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仍然接受,但在编译时会出现错误:
错误CS0619:'TestConsole.TestClass.Add(TestConsole.TestObject)'已废弃:'此类不支持此。'
答案 2 :(得分:6)
这听起来不错。 Liskov不会留下深刻印象。
如果你不希望DerivedClass的使用者能够访问方法DeriveClass.A()和DerivedClass.B()我会建议DerivedClass应该实现一些公共接口IWhateverMethodCIsAbout并且DerivedClass的消费者实际上应该与之交谈IWhateverMethodCIsAbout并且对BaseClass或DerivedClass的实现一无所知。
答案 3 :(得分:5)
您需要的是合成而非继承。
class Plane
{
public Fly() { .. }
public string GetPilot() {...}
}
现在,如果你需要一种特殊的平面,比如一个具有PairOfWings = 2但平面可以做的所有平面都可以..你继承平面。通过这种方式,您声明您的派生符合基类的约定,并且可以替换而不会在需要基类的任何地方闪烁。例如LogFlight(Plane)将继续使用BiPlane实例。
但是,如果您只想为要创建的新Bird创建Fly行为,并且不愿意支持完整的基类合约,那么您需要编写。在这种情况下,重构方法的行为以重用到新类型的Flight中。现在在Plane和Bird中创建并保持对此类的引用。 你没有继承,因为Bird不支持完整的基类合同......(例如,它不能提供GetPilot())。
出于同样的原因,在覆盖时不能降低基类方法的可见性。您可以在派生中覆盖并使基本私有方法公开,但反之亦然。例如在这个例子中,如果我派生出一种Plane“BadPlane”,然后覆盖并“隐藏”GetPilot() - 将其设为私有;一个客户端方法LogFlight(Plane p)适用于大多数Planes,但如果LogFlight的实现恰好需要/调用GetPilot(),它将会爆炸为“BadPlane”。 由于预期基类的所有派生都是“可替代的”,因此必须禁止这样做。
答案 4 :(得分:3)
我所知道的唯一方法是使用Has-A关系并仅实现您想要公开的函数。
答案 5 :(得分:3)
@Brian R. Bondy向我指了一篇关于隐藏继承和新关键字的有趣文章。
http://msdn.microsoft.com/en-us/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();
答案 6 :(得分:1)
隐藏是一个非常滑的斜坡。 IMO的主要问题是:
这取决于设计时间 实例的声明类型, 意思是你做的事情 然后,BaseClass obj = new SubClass() 调用obj.A(),隐藏被击败。将执行BaseClass.A()。
隐藏很容易模糊 行为(或行为改变) 基本类型。这显然是 当你拥有两者时,不要太担心 等式的两侧,或者如果调用'base.xxx'是你的子成员的一部分。
答案 7 :(得分:1)
我想说如果你有一个你想要做的代码库,它不是最好的代码库。它通常是需要某个公共签名的层次结构中的一个类的标志,而从该类派生的另一个类不需要它。
即将推出的编码范例称为“基于继承的组合”。这直接取决于面向对象开发的原则(特别是单一责任原则和开放/封闭原则)。
不幸的是,我们很多开发人员被教导面向对象的方式,我们已经养成了立即考虑继承而不是组合的习惯。我们倾向于拥有更大的类,这些类具有许多不同的职责,仅仅因为它们可能包含在相同的“真实世界”对象中。这可能导致深层次超过5级的类层次结构。
开发人员在处理继承时通常不会考虑的一个令人遗憾的副作用是,继承是您可以引入代码的最强依赖形式之一。您的派生类现在强烈依赖于它继承的类。这可能会使您的代码在长期内更加脆弱,并导致混淆问题,即更改基类中的某个行为会以不明显的方式破坏派生类。
破解代码的一种方法是通过另一个答案中提到的接口。这是一件很聪明的事情,因为你希望类的外部依赖项绑定到抽象,而不是具体/派生类型。这允许您在不更改接口的情况下更改实现,所有这些都不会影响依赖类中的一行代码。
我宁愿维护一个拥有数百/数千甚至更多类的系统,这些系统都是小型且松散耦合的,而不是处理大量使用多态/继承的系统,并且具有更少紧密耦合的类。
对象导向开发中的最佳资源可能就是Robert C. Martin的书,Agile Software Development, Principles, Patterns, and Practices。
答案 8 :(得分:0)
如果它们在原始类中被公开定义,则不能将它们覆盖为派生类中的私有。但是,您可以使public方法抛出异常并实现您自己的私有函数。
编辑:Jorge Ferreira是对的。
答案 9 :(得分:0)
虽然这个问题的答案是“不”,但我想指出其他人来到这里(因为OP有点暗示第三方的集会访问)。当其他人引用一个程序集时,Visual Studio应该遵循以下属性,因此它不会显示在intellisense中(隐藏,但仍然可以调用,所以要小心):
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
如果您别无选择,则应该能够在隐藏基本类型方法的方法上使用new
,返回=> throw new NotSupportedException();
,并将其与上述属性结合使用。
另一个技巧取决于NOT尽可能从基类继承,其中base具有相应的接口(例如IList<T>
用于List<T>
)。 “显式”实现接口也会在类类型上隐藏intellisense中的那些方法。例如:
public class GoodForNothing: IDisposable
{
void IDisposable.Dispose() { ... }
}
对于var obj = new GoodForNothing()
,Dispose()
方法将无法在obj
上使用obj
方法。但是,对于明确键入演员IDisposable
到public class MyList<T> : IList<T>
{
List<T> _Items = new List<T>();
public T this[int index] => _Items[index];
public int Count => _Items.Count;
public void Add(T item) => _Items.Add(item);
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
void ICollection<T>.Clear() => throw new InvalidOperationException("No you may not!"); // (hidden)
/*...etc...*/
}
的任何人都可以使用它。
此外,您还可以包装基类型而不是继承它,然后隐藏一些方法:
GetInstance(bool)