44

C# 是否有私有/受保护继承的概念,如果没有,为什么?

C++


class Foo : private Bar {
 public:
   ...
 }; 

C#


public abstract NServlet class : private System.Web.UI.Page
{
    // error "type expected"
}

我在 .aspx 页面中实现“类似 servlet”的概念,我不希望具体类能够查看 System.Web.UI.Page 基础的内部结构。

4

10 回答 10

20

C# 只允许公共继承。C++ 允许所有这三种。公共继承意味着“IS-A”类型的关系,而私有继承意味着“Is-Implemented-In-Terms-Of”类型的关系。由于分层(或组合)以一种可以说更简单的方式实现了这一点,因此只有在受保护的成员或虚函数绝对需要时才使用私有继承 - 根据 Scott Meyers 在 Effective C++ 第 42 条中的说法。

我的猜测是,C# 的作者并不认为这种根据另一个类来实现一个类的附加方法是必要的。

于 2008-08-28T19:30:30.787 回答
8

不,它没有。允许这种类型的限制有什么好处?

私有和受保护的继承有利于封装(信息隐藏)。在 C++ 中支持受保护的*继承,但在 Java 中不支持。这是我项目中的一个示例,它会很有用。

作为 3rd 方框架**有一个基类。它有许多设置以及用于操作它们的属性和方法。基类在分配单个设置时不会进行大量检查,但如果遇到不可接受的组合,它会在稍后生成异常。

我正在使用分配这些设置的方法创建一个子类(例如,从文件中分配精心设计的设置)。拒绝其余代码(在我的子类之外)操纵单个设置并将它们弄乱的能力会很好。

也就是说,我认为在 C++ 中(同样支持私有和受保护的继承),可以将子类转换为父类并访问父类的公共成员。(另请参阅Chris Karcher 的帖子)不过,受保护的继承改进了信息隐藏。如果类 B1 的成员需要真正隐藏在其他类 C1 和 C2 中,可以通过在 C1 和 C2 中创建类 B1 的受保护变量来安排。B1 的受保护实例将可供 C1 和 C2 的子级使用。当然,这种方法本身并不提供 C1 和 C2 之间的多态性。但是可以通过从公共接口 I1 继承 C1 和 C2 来添加多态性(如果需要)。

*** 为简洁起见,将使用“受保护”而不是“私有和受保护”。

** 就我而言,是 National Instruments Measurement Studio。

  • 缺口
于 2010-02-23T21:05:57.210 回答
5

您可以通过将类中的同一成员声明为私有并使用 new 关键字来隐藏继承的 API,使其不公开可见。请参阅通过MSDN 的继承隐藏。

于 2008-08-28T19:11:55.963 回答
3

如果您希望 NServlet 类对 Page 不了解任何信息,您应该考虑使用适配器模式。编写一个将承载 NServlet 类实例的页面。然后,根据您正在做什么,您可以编写大量只知道基类 NServlet 的类,而不必用 asp.net 页面成员污染您的 API。

于 2008-08-28T19:13:31.560 回答
3

@bdukes:请记住,您并没有真正隐藏该成员。例如:

class Base
{
   public void F() {}
}
class Derived : Base
{
   new private void F() {}
}

Base o = new Derived();
o.F();  // works

但这与 C++ 中的私有继承相同,这正是提问者想要的。

于 2008-08-28T19:30:45.170 回答
1

不,仅限公共继承。

于 2008-08-28T19:03:38.557 回答
1

您可能想要一个与 NServlet 实现挂钩的 ServletContainer 类。在我的书中,不允许私有/受保护的继承并不是什么大问题,并且可以减少语言的混乱——使用 LINQ 等。我们已经有足够的东西要记住了。

于 2008-08-28T19:10:00.617 回答
1

我知道这是一个老问题,但我在编写 C# 时多次遇到这个问题,我想知道......为什么不只使用接口?

当您创建第 3 方框架类的子类时,还要让它实现一个公共接口。然后定义该接口以仅包含您希望客户端访问的方法。然后,当客户端请求该类的实例时,给他们一个该接口的实例。

这似乎是 C# 接受的做这些事情的方式。

我第一次这样做是在我意识到 C# 标准库没有字典的只读变体时。我想提供对字典的访问,但不想让客户能够更改字典中的项目。所以我定义了一个“class DictionaryEx<K,V,IV> : Dictionary<K,V>, IReadOnlyDictionary<K,IV> where V : IV”,其中 K 是键类型,V 是实值类型,IV 是防止更改的 V 类型的接口。DictionaryEx 的实现非常简单。唯一困难的部分是创建一个 ReadOnlyEnumerator 类,但即使这样也没有花费很长时间。

我可以看到这种方法的唯一缺点是客户端是否尝试将您的公共接口动态转换为相关的子类。要阻止这种情况,请将您的课程设置为内部课程。如果您的客户将您的公共接口转换为原始基类,我认为他们很清楚他们将自己的生命掌握在自己手中。:-)

于 2012-06-23T17:15:07.240 回答
1

第一个解决方案:

protected internal在同一个程序集中作为 public 并在其他程序集中受保护。

您需要更改不会通过继承公开的类的每个成员的访问修饰符。

尽管此解决方案要求并强制该类被继承以供另一个程序集使用,但它有点限制。因此,是否仅通过继承使用的选择由不知情的父母选择......通常孩子们更了解架构......

这不是一个完美的解决方案,但可能是添加接口以隐藏方法的更好选择,并且仍然可以通过子类隐藏使用父方法的可能性,因为您可能无法轻易强制使用接口。


问题:

protectedprivate访问修饰符不能用于实现接口的方法。这意味着受保护的内部解决方案不能用于接口实现的方法。这是一个很大的限制。


最终解决方案:

我退回到隐藏方法的接口解决方案。

它的问题是能够强制使用界面,以便隐藏的成员总是被隐藏,然后绝对避免错误。

强制只使用接口,只需使构造函数受保护添加一个静态构造方法(我将其命名为 New)。这个静态 New 方法实际上是一个工厂函数,它返回接口。所以剩下的代码只能使用接口!

于 2015-09-16T17:15:26.440 回答
-3

不,它没有。允许这种类型的限制有什么好处?

于 2008-08-28T19:11:40.083 回答