我真的不明白。
如果基类是抽象的并且仅用于为程序集中定义的公共子类提供通用功能,为什么不应该将其声明为内部的?
我不希望抽象类对程序集外部的代码可见。我不想让外部代码知道它。
我真的不明白。
如果基类是抽象的并且仅用于为程序集中定义的公共子类提供通用功能,为什么不应该将其声明为内部的?
我不希望抽象类对程序集外部的代码可见。我不想让外部代码知道它。
更新:这个问题是我 2012 年 11 月 13 日博客的主题。有关此问题的更多想法,请参见它。谢谢你的好问题!
你是对的; 它不必是这样的。其他 OO 语言允许“私有继承”,即 D 从 B 继承的事实只能被能够看到 B 的代码利用。
这是最初的 C# 设计者的设计决定。不幸的是,我现在不在办公桌前——我将在长周末休假几天——所以我面前没有 1999 年的语言设计笔记。如果我回来后想到它,我会浏览它们,看看是否有理由做出这个决定。
我个人的看法是,应该用继承来表示“是一种”的关系;也就是说,继承应该表示用语言建模的域的语义。我尽量避免使用继承作为代码共享机制的情况。正如其他人所提到的,如果您要表示的是“这个类与其他类共享实现机制”,那么最好选择组合而不是继承。
通过从一个类继承,您可以通过您的子类公开基类的功能。
由于子类比其父类具有更高的可见性,因此您将暴露本应受到保护的成员。
您不能通过实现具有更高可见性的子类来违反父类的保护级别。
如果基类确实要供公共子类使用,那么您也需要将父类设为公共。
另一种选择是保持您的“父”内部,使其非抽象,并使用它来组成您的子类,并使用接口强制类实现功能:
public interface ISomething
{
void HelloWorld();
}
internal class OldParent : ISomething
{
public void HelloWorld(){ Console.WriteLine("Hello World!"); }
}
public class OldChild : ISomething
{
OldParent _oldParent = new OldParent();
public void HelloWorld() { _oldParent.HelloWorld(); }
}
我认为您可以做的最接近的事情是通过将其构造函数设为内部来防止其他程序集创建抽象类,引用MSDN:
内部构造函数可防止抽象类用作与抽象类不在同一程序集中的类型的基类。
然后,您可以尝试将EditorBrowsableAttribute添加到该类中以尝试将其从 IntelliSense 中隐藏(尽管说实话,我使用它的结果好坏参半)或将基类放在嵌套命名空间中,例如MyLibrary.Internals
将其与其余部分分开你的课。
我认为您在这里混淆了关注点,实际上应该归咎于 C#(以及之前的 Java)。
继承应该作为一种分类机制,而它通常用于代码重用。
对于代码重用来说,组合优于继承是众所周知的。C# 的问题在于它为我们提供了一种简单的继承方式:
class MyClass : MyReusedClass { }
但为了作曲,我们需要自己动手:
class MyClass {
MyReusedClass _reused;
// need to expose all the methods from MyReusedClass and delegate to _reused
}
缺少的是像trait (pdf)这样的构造,它将使组合达到与继承相同的可用性级别。
有关于C# (pdf) 中的特征的研究,它看起来像这样:
class MyClass {
uses { MyTrait; }
}
虽然我希望看到另一个模型(Perl 6 角色的模型)。
更新:
附带说明一下,Oxygene 语言有一个特性,可以让您将接口的所有成员委托给实现该接口的成员属性:
type
MyClass = class(IReusable)
private
property Reused : IReusable := new MyReusedClass(); readonly;
implements public IReusable;
end;
在这里,所有的接口成员都IReusable
将被暴露出来MyClass
,它们都将委托给该Reused
属性。但是,这种方法存在一些问题。
另一个更新:
我已经开始在 C# 中实现这个自动组合概念:看看NRoles。
我认为这会违反Liskov 替换原则。
在这种情况下,我使用了内部类并且更喜欢组合而不是继承。您的设计是否有任何内容禁止在内部类中包含所有此类功能,然后让您的公共类包含此内部类的实例?