C# - 可以隐藏公共继承的方法(例如,对派生类进行私有)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/106383/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C# - Can publicly inherited methods be hidden (e.g. made private to derived class)
提问by Lyndon
Suppose I have BaseClass with public methods A and B, and I create DerivedClass through inheritance.
假设我有带有公共方法 A 和 B 的 BaseClass,并且我通过继承创建了 DerivedClass。
e.g.
例如
public DerivedClass : BaseClass {}
Now I want to develop a method C in DerivedClass that uses A and B. Is there a way I can override methods A and B to be private in DerivedClass so that only method C is exposed to someone who wants to use my DerivedClass?
现在我想在 DerivedClass 中开发一个使用 A 和 B 的方法 C。有没有办法我可以覆盖方法 A 和 B 在 DerivedClass 中是私有的,以便只有方法 C 暴露给想要使用我的 DerivedClass 的人?
采纳答案by Brian R. Bondy
It's not possible, why?
这是不可能的,为什么?
In C#, it is forced upon you that if you inherit public methods, you must make them public. Otherwise they expect you not to derive from the class in the first place.
在 C# 中,如果您继承公共方法,则必须将它们设为公共方法。否则,他们首先希望您不要从该类派生。
Instead of using the is-a relationship, you would have to use the has-a relationship.
您必须使用 has-a 关系,而不是使用 is-a 关系。
The language designers don't allow this on purpose so that you use inheritance more properly.
语言设计者故意不允许这样做,以便您更正确地使用继承。
For example one might accidentally confuse a class Car to derive from a class Engine to get it's functionality. But an Engine is functionality that is used by the car. So you would want to use the has-a relationship. The user of the Car does not want to have access to the interface of the Engine. And the Car itself should not confuse the Engine's methods with it's own. Nor Car's future derivations.
例如,人们可能会意外地将类 Car 混淆为从类 Engine 派生以获取其功能。但是引擎是汽车使用的功能。所以你会想要使用 has-a 关系。Car 的用户不想访问 Engine 的界面。并且 Car 本身不应该将 Engine 的方法与其自己的方法混淆。也不是 Car 的未来派生。
So they don't allow it to protect you from bad inheritance hierarchies.
因此,他们不允许它保护您免受不良继承层次结构的影响。
What should you do instead?
你应该怎么做?
Instead you should implement interfaces. This leaves you free to have functionality using the has-a relationship.
相反,您应该实现接口。这使您可以使用 has-a 关系自由地使用功能。
Other languages:
其他语言:
In C++ you simply specify a modifier before the base class of private, public or protected. This makes all members of the base that were public to that specified access level. It seems silly to me that you can't do the same in C#.
在 C++ 中,您只需在 private、public 或 protected 基类之前指定一个修饰符。这使得基础的所有成员对指定的访问级别都是公开的。在我看来,你不能在 C# 中做同样的事情似乎很愚蠢。
The restructured code:
重组后的代码:
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;
}
I understand the names of the above classes are a little moot :)
我知道上述课程的名称有点没有实际意义:)
Other suggestions:
其他建议:
Others have suggested to make A() and B() public and throw exceptions. But this doesn't make a friendly class for people to use and it doesn't really make sense.
其他人建议公开 A() 和 B() 并抛出异常。但这并不能成为人们使用的友好类,也没有任何意义。
回答by Cody Brocious
If they're defined public in the original class, you cannot override them to be private in your derived class. However, you could make the public method throw an exception and implement your own private function.
如果它们在原始类中被定义为 public,则不能在派生类中将它们覆盖为私有。但是,您可以使公共方法抛出异常并实现您自己的私有函数。
Edit: Jorge Ferreira is correct.
编辑:豪尔赫费雷拉是正确的。
回答by Nescio
The only way to do this that I know of is to use a Has-A relationship and only implement the functions you want to expose.
我所知道的唯一方法是使用 Has-A 关系并仅实现您想要公开的功能。
回答by Jorge Ferreira
@Brian R. Bondy pointed me to an interesting article on Hiding through inheritance and the newkeyword.
@Brian R. Bondy 向我指出了一篇关于隐藏继承和new关键字的有趣文章。
http://msdn.microsoft.com/en-us/library/aa691135(VS.71).aspx
http://msdn.microsoft.com/en-us/library/aa691135(VS.71).aspx
So as workaround I would suggest:
因此,作为解决方法,我建议:
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();
}
}
This way code like this will throw a NotSupportedException:
这样的代码会抛出一个NotSupportedException:
DerivedClass d = new DerivedClass();
d.A();
回答by Hamish Smith
That sounds like a bad idea. Liskovwould not be impressed.
这听起来是个坏主意。利斯科夫不会留下深刻印象。
If you don't want consumers of DerivedClass to be able to access methods DeriveClass.A() and DerivedClass.B() I would suggest that DerivedClass should implement some public interface IWhateverMethodCIsAbout and the consumers of DerivedClass should actually be talking to IWhateverMethodCIsAbout and know nothing about the implementation of BaseClass or DerivedClass at all.
如果您不希望 DerivedClass 的使用者能够访问方法 DeriveClass.A() 和 DerivedClass.B() 我建议 DerivedClass 应该实现一些公共接口 IWhateverMethodCIsAbout 并且 DerivedClass 的使用者实际上应该与 IWhateverMethodCIsAbout 交谈并知道根本没有关于 BaseClass 或 DerivedClass 的实现。
回答by Jared
Hiding is a pretty slippery slope. The main issues, IMO, are:
隐藏是一个非常滑的斜坡。IMO 的主要问题是:
It's dependent upon the design-time declaration type of the instance, meaning if you do something like BaseClass obj = new SubClass(), then call obj.A(), hiding is defeated. BaseClass.A() will be executed.
Hiding can very easily obscure behavior (or behavior changes) in the base type. This is obviously less of a concern when you own both sides of the equation, or if calling 'base.xxx' is part of your sub-member.
- If you actually doown both sides of the base/sub-class equation, then you should be able to devise a more manageable solution than institutionalized hiding/shadowing.
它取决于实例的设计时声明类型,这意味着如果您执行诸如 BaseClass obj = new SubClass() 之类的操作,然后调用 obj.A(),隐藏就会失败。BaseClass.A() 将被执行。
隐藏可以很容易地掩盖基类型中的行为(或行为改变)。当你拥有等式的两边,或者如果调用“base.xxx”是你的子成员的一部分时,这显然不是一个问题。
- 如果你真的做自己底座/次一级方程式的两边,那么你应该能够设计出比制度化隐藏一个更易于管理的解决方案/阴影。
回答by Jason Olson
I would say that if you have a codebase that you are wanting to do this with, it is not the best designed code base. It's typically a sign of a class in one level of the heirarchy needing a certain public signature while another class derived from that class doesn't need it.
我想说的是,如果你有一个代码库,你想用它来做这件事,它不是设计最好的代码库。它通常是层次结构中某个级别的类的标志,需要某个公共签名,而从该类派生的另一个类不需要它。
An upcoming coding paradigm is called "Composition over Inheritance." This plays directly off of the principles of object-oriented development (especially the Single Responsibility Principle and Open/Closed Principle).
即将到来的编码范式称为“组合优于继承”。这直接违背了面向对象开发的原则(尤其是单一职责原则和开放/封闭原则)。
Unfortunately, the way a lot of us developers were taught object-orientation, we have formed a habit of immediately thinking about inheritance instead of composition. We tend to have larger classes that have many different responsibilities simply because they might be contained with the same "Real World" object. This can lead to class hierarchies that are 5+ levels deep.
不幸的是,我们很多开发人员都被教授面向对象的方式,我们已经形成了立即考虑继承而不是组合的习惯。我们倾向于拥有具有许多不同职责的较大类,仅仅是因为它们可能包含在同一个“真实世界”对象中。这可能会导致 5 级以上的类层次结构。
An unfortunate side-effect that developers don't normally think about when dealing with inheritance is that inheritance forms one of the strongest forms of dependencies that you can ever introduce into your code. Your derived class is now strongly dependant on the class it was inherited from. This can make your code more brittle in the long run and lead to confounding problems where changing a certain behavior in a base class breaks derived classes in obscure ways.
开发人员在处理继承时通常不会考虑的一个不幸的副作用是,继承形成了您可以引入到代码中的最强大的依赖形式之一。您的派生类现在强烈依赖于它继承自的类。从长远来看,这会使您的代码更加脆弱,并导致混淆问题,即更改基类中的某些行为会以晦涩的方式破坏派生类。
One way to break your code up is through interfaces like mentioned in another answer. This is a smart thing to do anyways as you want a class's external dependencies to bind to abstractions, not concrete/derived types. This allows you to change the implementation without changing the interface, all without effecting a line of code in your dependent class.
分解代码的一种方法是通过另一个答案中提到的接口。无论如何,这是一件明智的事情,因为您希望类的外部依赖项绑定到抽象,而不是具体/派生类型。这允许您在不更改接口的情况下更改实现,所有这一切都不会影响依赖类中的一行代码。
I would much rather than maintain a system with hundreds/thousands/even more classes that are all small and loosely-coupled, than deal with a system that makes heavy use of polymorphism/inheritance and has fewer classes that are more tightly coupled.
我宁愿维护一个包含数百/数千/甚至更多类都是小而松散耦合的系统,也不愿处理一个大量使用多态性/继承并且具有更少类的更紧密耦合的系统。
Perhaps the bestresource out there on object-oriented development is Robert C. Martin's book, Agile Software Development, Principles, Patterns, and Practices.
也许关于面向对象开发的最佳资源是 Robert C. Martin 的书《敏捷软件开发、原则、模式和实践》。
回答by Gishu
What you need is composition not inheritance.
你需要的是组合而不是继承。
class Plane
{
public Fly() { .. }
public string GetPilot() {...}
}
Now if you need a special kind of Plane, such as one that has PairOfWings = 2 but otherwise does everything a plane can.. You inherit plane. By this you declare that your derivation meets the contract of the base class and can be substituted without blinking wherever a base class is expected. e.g. LogFlight(Plane) would continue to work with a BiPlane instance.
现在,如果您需要一种特殊类型的飞机,例如具有 PairOfWings = 2 的飞机,但除此之外还可以执行飞机可以执行的所有操作。您继承了飞机。通过这种方式,您可以声明您的派生符合基类的约定,并且可以在需要基类的任何地方进行替换而不会闪烁。例如 LogFlight(Plane) 将继续使用 BiPlane 实例。
However if you just need the Fly behavior for a new Bird you want to create and are not willing to support the complete base class contract, you compose instead. In this case, refactor the behavior of methods to reuse into a new type Flight. Now create and hold references to this class in both Plane and Bird. You don't inherit because the Bird does not support the complete base class contract... ( e.g. it cannot provide GetPilot() ).
但是,如果您只需要要创建的新 Bird 的 Fly 行为,并且不愿意支持完整的基类契约,则可以进行组合。在这种情况下,将要重用的方法的行为重构为新类型 Flight。现在在 Plane 和 Bird 中创建并保存对此类的引用。你不继承是因为 Bird 不支持完整的基类契约......(例如它不能提供 GetPilot() )。
For the same reason, you cannot reduce the visibility of base class methods when you override..you can override and make a base private method public in the derivation but not vice versa. e.g. In this example, if I derive a type of Plane "BadPlane" and then override and "Hide" GetPilot() - make it private; a client method LogFlight(Plane p) will work for most Planes but will blow up for "BadPlane" if the implementation of LogFlight happens to need/call GetPilot(). Since all derivations of a base class are expected to be 'substitutable' wherever a base class param is expected, this has to be disallowed.
出于同样的原因,当您覆盖时,您不能降低基类方法的可见性。您可以在派生中覆盖并公开基类私有方法,反之亦然。例如,在这个例子中,如果我派生出一种飞机类型“BadPlane”,然后覆盖并“隐藏”GetPilot() - 将其设为私有;客户端方法 LogFlight(Plane p) 将适用于大多数飞机,但如果 LogFlight 的实现碰巧需要/调用 GetPilot(),则会因“BadPlane”而爆炸。由于基类的所有派生都应该在需要基类参数的任何地方都是“可替代的”,因此必须禁止这样做。
回答by nono
When you, for instance, try to inherit from a List<object>
, and you want to hide the direct Add(object _ob)
member:
例如,当您尝试从 a 继承时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!!");
}
It's not really the most preferable solution, but it does the job. Intellisense still accepts, but at compile time you get an error:
这并不是最可取的解决方案,但它可以完成工作。Intellisense 仍然接受,但在编译时会出现错误:
error CS0619: 'TestConsole.TestClass.Add(TestConsole.TestObject)' is obsolete: 'This is not supported in this class.'
错误 CS0619:“TestConsole.TestClass.Add(TestConsole.TestObject)”已过时:“此类不支持此功能。”
回答by James Wilkins
While the answer to the question is "no", there is one tip I wish to point out for others arriving here (given that the OP was sort of alluding to assembly access by 3rd parties). When others reference an assembly, Visual Studio should be honoring the following attribute so it will not show in intellisense (hidden, but can STILL be called, so beware):
虽然问题的答案是“否”,但我想向其他到达这里的其他人指出一个提示(鉴于 OP 有点暗指第 3 方的组装访问)。当其他人引用一个程序集时,Visual Studio 应该遵循以下属性,这样它就不会在智能感知中显示(隐藏,但仍然可以被调用,所以要小心):
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
If you had no other choice, you should be able to use new
on a method that hides a base type method, return => throw new NotSupportedException();
, and combine it with the attribute above.
如果您别无选择,您应该能够new
在隐藏基类型方法的方法上使用 return => throw new NotSupportedException();
,并将其与上述属性组合。
Another trick depends on NOT inheriting from a base class if possible, where the base has a corresponding interface (such as IList<T>
for List<T>
). Implementing interfaces "explicitly" will also hide those methods from intellisense on the class type. For example:
另一个技巧取决于尽可能不从基类继承,其中基类具有相应的接口(例如IList<T>
for List<T>
)。“显式”实现接口也会在类类型的智能感知中隐藏这些方法。例如:
public class GoodForNothing: IDisposable
{
void IDisposable.Dispose() { ... }
}
In the case of var obj = new GoodForNothing()
, the Dispose()
method will not be available on obj
. However, it WILL be available to anyone who explicitly type-casts obj
to IDisposable
.
在 的情况下var obj = new GoodForNothing()
,该Dispose()
方法将不可用obj
。但是,任何明确类型转换obj
为IDisposable
.
In addition, you could also wrap a base type instead of inheriting from it, then hide some methods:
此外,您还可以包装一个基类型而不是从它继承,然后隐藏一些方法:
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...*/
}