web-dev-qa-db-ja.com

パブリッククラスが内部クラスを拡張できないのはなぜですか?

本当にわかりません。

基本クラスが抽象であり、アセンブリで定義されたパブリックサブクラスに共通の機能を提供するためだけに使用されることを意図している場合、それを内部で宣言してはならないのはなぜですか?

アセンブリの外部のコードから抽象クラスが見えるようにしたくありません。外部コードにそれを知らせたくありません。

47
David

クラスから継承することにより、子を通じて基本クラスの機能を公開します。

子クラスは親よりも可視性が高いため、保護されているメンバーを公開することになります。

視認性の高い子を実装することで、親クラスの保護レベルに違反することはできません。

基本クラスが本当にパブリックの子クラスで使用されることを意図している場合は、親もパブリックにする必要があります。

もう1つのオプションは、「親」を内部に保ち、非抽象化し、それを使用して子クラスを作成し、インターフェイスを使用してクラスに機能を実装させることです。

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(); }
}
38
Justin Niessner

更新:この質問は 2012年11月13日の私のブログの主題 でした。この問題に関するいくつかの考えについては、それを参照してください。すばらしい質問をありがとう!


あなたが正しい;そのようにする必要はありません。その他OO言語は「プライベート継承」を許可します。これにより、DがBから継承するという事実は、Bを参照する機能を持つコードによってのみ利用できます。

これは、元のC#デザイナーの設計上の決定でした。残念ながら、今は机から離れています-長い週末のために数日休みます-そのため、目の前にある1999年の言語デザインノートを持っていません。私が戻ってきたときにそれを考えたら、それらを参照して、この決定の正当性があるかどうかを確認します。

私の個人的な意見では、継承は「一種の」関係を表すために使用する必要があります。つまり、継承は、言語でモデル化されているドメインのセマンティクスを表す必要があります。 コード共有メカニズムとして継承が使用される状況を避けるようにしています。他の人が述べたように、表現したいものが「このクラスは他のクラスと実装メカニズムを共有している」場合は、継承よりも合成を優先するのがおそらく最善です。

40
Eric Lippert

あなたができる最も近いことは、コンストラクタを内部にして抽象クラスを作成する他のアセンブリが [〜#〜] msdn [〜#〜] から引用するのを防ぐことだと思います。

内部コンストラクターは、抽象クラスが、抽象クラスと同じAssemblyにない型の基本クラスとして使用されるのを防ぎます。

次に、クラスに EditorBrowsableAttribute を追加してIntellisenseから非表示にしてみてください(ただし、正直に言うと混合結果が出てきました)、または基本クラスをネストされた名前空間に配置します。なので MyLibrary.Internals残りのクラスから分離します。

16
Samuel

私はあなたがここで懸念を混ぜていると思います、そして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の役割のもの)を見たいのですが。

UPDATE:

補足として、Oxygene言語には a feature があり、インターフェースのすべてのメンバーを、そのインターフェースを実装するメンバープロパティに委任できます。

type
  MyClass = class(IReusable)
  private
    property Reused : IReusable := new MyReusedClass(); readonly;
      implements public IReusable;
  end;

ここでは、IReusableのすべてのインターフェイスメンバーがMyClassを通じて公開され、それらはすべてReusedプロパティに委任されます。ただし、このアプローチには 問題 がいくつかあります。

別の更新:

この自動構成の概念をC#で実装し始めました: NRoles を見てください。

4
Jordão

これは Liskov Substitution Principle に違反すると思います。

このような場合、私は内部クラスを使用しており、継承よりも構成を優先しています。そのような機能をすべて内部クラスに含めることを禁止し、パブリッククラスにこの内部クラスのインスタンスを含めることを禁止する設計について何かありますか?

3
Dave