C#-可以公开的继承的方法可以隐藏的(例如由私人来源类)
题
假设我有BaseClass与公共方法A和B、我建立的基类实现通过继承。
例如
public DerivedClass : BaseClass {}
现在我想要开发一种方法C中的基类实现一个使用和B。有没有一种方式我可以替代方法A和B可以在私人基类实现这样,唯一的方法C是暴露一个人想要利用我的的基类实现?
解决方案
这不可能,为什么?
在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()公开并抛出异常。但是,这并没有为人们提供友好的课程,而且实际上没有意义。
其他提示
例如,当您尝试从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)'已废弃:'此类不支持此。'
这听起来不错。 Liskov 不会留下深刻印象。
如果你不希望DerivedClass的使用者能够访问方法DeriveClass.A()和DerivedClass.B()我会建议DerivedClass应该实现一些公共接口IWhateverMethodCIsAbout并且DerivedClass的消费者实际上应该与之交谈IWhateverMethodCIsAbout并且对BaseClass或DerivedClass的实现一无所知。
您需要的是合成而非继承。
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但会爆炸<!>“BadPlane <!>”;如果LogFlight的实现恰好需要/调用GetPilot()。 由于预期基类的所有派生都是“可替代的”,因此必须禁止这样做。
我所知道的唯一方法是使用Has-A关系并仅实现您想要公开的函数。
@Brian R. Bondy向我指了一篇关于隐藏继承和新关键字的有趣文章。
http://msdn.microsoft.com/ EN-US /库/ 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();
隐藏的是一个非常滑坡。主要的问题,海事组织是:
它依赖的设计时 《宣言》类型的实例, 这意味着如果你不喜欢的东西 BaseClass obj=new类(),然后 呼叫obj。一个(),隐藏被击败。BaseClass.一个()将被执行。
躲在可以很容易模糊 行为(或行为变化)在 基类型。这显然是 少的关切,当你自己的两个 双方式,或者如果打电话的基础。xxx'是部分的子部件。
- 如果你真的 做 自己的两侧的基地/子类的公式,然后你应该能够制定一个更易于管理解决方案制度化躲/阴影。
我想说如果你有一个你想要这样做的代码库,它就不是设计最好的代码库。它通常是需要某个公共签名的层次结构中的一个类的标志,而从该类派生的另一个类不需要它。
即将推出的编码范例称为<!>“;基于继承的组合。<!>”;这直接取决于面向对象开发的原则(特别是单一责任原则和开放/封闭原则)。
不幸的是,我们很多开发人员被教导面向对象的方式,我们已经养成了立即考虑继承而不是组合的习惯。我们倾向于拥有更大的类,这些类具有许多不同的职责,仅仅因为它们可能包含在相同的<!>“真实世界<!>”中。宾语。这可能导致深层次超过5级的类层次结构。
开发人员在处理继承时通常不会考虑的一个令人遗憾的副作用是,继承是您可以引入代码的最强依赖形式之一。您的派生类现在强烈依赖于它继承的类。这可能会使您的代码在长期内更加脆弱,并导致混淆问题,即更改基类中的某个行为会以不明显的方式破坏派生类。
破解代码的一种方法是通过另一个答案中提到的接口。这是一件很聪明的事情,因为你希望类的外部依赖项绑定到抽象,而不是具体/派生类型。这允许您在不更改接口的情况下更改实现,所有这些都不会影响依赖类中的一行代码。
我宁愿维护一个拥有数百/数千甚至更多类的系统,这些系统都是小型且松散耦合的,而不是处理大量使用多态/继承的系统,并且具有更少紧密耦合的类。
面向对象开发的最佳资源可能是Robert C. Martin的书,敏捷软件开发,原则,模式和实践。
如果它们在原始类中被公开定义,则不能将它们覆盖为派生类中的私有。但是,您可以使public方法抛出异常并实现您自己的私有函数。
编辑:Jorge Ferreira是对的。
虽然这个问题的答案是<!>“no <!>”,但我想指出其他人来到这里(因为OP有点暗示第三方的装配访问) )。当其他人引用一个程序集时,Visual Studio应该遵循以下属性,因此它不会显示在intellisense中(隐藏,但仍然可以调用,所以要小心):
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
如果您没有其他选择,您应该能够在隐藏基本类型方法的方法上使用new
,返回=> throw new NotSupportedException();
,并将其与上述属性结合使用。
另一个技巧取决于NOT尽可能从基类继承,其中base具有相应的接口(例如IList<T>
for List<T>
)。实现接口<!>显式<!>;还会在类类型上隐藏intellisense中的那些方法。例如:
public class GoodForNothing: IDisposable
{
void IDisposable.Dispose() { ... }
}
如果是var obj = new GoodForNothing()
,Dispose()
方法将无法在obj
上使用。但是,对于明确键入IDisposable
to <=>的任何人都可以使用它。
此外,您还可以包装基类型而不是继承它,然后隐藏一些方法:
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...*/
}