66

Suppose I have BaseClass with public methods A and B, and I create DerivedClass through inheritance.

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?

4

10 に答える 10

78

それは不可能です、なぜですか?

C# では、パブリック メソッドを継承する場合は、それらをパブリックにする必要があります。それ以外の場合、そもそもクラスから派生しないことを期待しています。

is-a 関係を使用する代わりに、has-a 関係を使用する必要があります。

言語設計者は、継承をより適切に使用するために、意図的にこれを許可していません。

たとえば、クラス Car がクラス Engine から派生してその機能を取得すると誤って混同する場合があります。しかし、エンジンは車で使用される機能です。したがって、has-a 関係を使用する必要があります。車のユーザーは、エンジンのインターフェースにアクセスしたくありません。また、車自体がエンジンのメソッドとそれ自身のメソッドを混同してはなりません。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() を公開して例外をスローすることを提案する人もいます。しかし、これは人々が使いやすいクラスにはならず、あまり意味がありません。

于 2008-09-19T23:56:51.000 に答える
33

たとえば、から継承しようとして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)' は廃止されました: 'これはこのクラスではサポートされていません。

于 2010-07-01T10:06:57.593 に答える
7

それは悪い考えのように聞こえます。Liskovは感心しませんでした。

DerivedClass のコンシューマーがメソッド DeriveClass.A() および DerivedClass.B() にアクセスできないようにする場合は、DerivedClass がパブリック インターフェイス IWhateverMethodCIsAbout を実装し、DerivedClass のコンシューマーが実際に IWhateverMethodCIsAbout と通信して知っている必要があることをお勧めします。 BaseClass または DerivedClass の実装についてはまったく何もありません。

于 2008-09-19T23:35:43.187 に答える
5

必要なのは、継承ではなく構成です。

class Plane
{
  public Fly() { .. }
  public string GetPilot() {...}
}

ここで、PairOfWings = 2 のような特別な種類の平面が必要であるが、それ以外の場合は平面ができるすべてのことを行う場合.. 平面を継承します。これにより、派生が基本クラスの契約を満たしていることを宣言し、基本クラスが期待される場所で点滅することなく置換できます。たとえば、LogFlight(Plane) は引き続き BiPlane インスタンスで動作します。

ただし、作成する新しい Bird の Fly 動作だけが必要で、完全な基本クラス コントラクトをサポートする気がない場合は、代わりに構成します。この場合、メソッドの動作をリファクタリングして、新しいタイプの Flight に再利用します。Plane と Bird の両方で、このクラスへの参照を作成して保持します。Bird は完全な基本クラス コントラクトをサポートしていないため、継承しません (たとえば、 GetPilot() を提供できません)。

同じ理由で、オーバーライドするときに基本クラス メソッドの可視性を減らすことはできません。派生で基本プライベート メソッドをオーバーライドしてパブリックにすることはできますが、その逆はできません。たとえば、この例では、平面のタイプ「BadPlane」を派生させてから、GetPilot() をオーバーライドして「非表示」にすると、プライベートになります。クライアント メソッド LogFlight(Plane p) はほとんどの Planes で機能しますが、LogFlight の実装で GetPilot() が必要/呼び出された場合、"BadPlane" で失敗します。基本クラスのすべての派生物は、基本クラスのパラメーターが期待される場所ならどこでも「置換可能」であることが期待されるため、これは許可されていません。

于 2008-09-20T05:18:18.887 に答える
4

@Brian R. Bondy は、継承とnewキーワードによる非表示に関する興味深い記事を教えてくれました。

http://msdn.microsoft.com/en-us/library/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();
于 2008-09-19T23:34:33.423 に答える
3

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.

于 2008-09-19T23:31:32.777 に答える
1

隠蔽はかなり滑りやすい斜面です。主な問題、IMO は次のとおりです。

  • これは、インスタンスのデザイン時の宣言タイプに依存します。つまり、BaseClass obj = new SubClass() のような処理を行ってから obj.A() を呼び出すと、非表示が無効になります。BaseClass.A() が実行されます。

  • 非表示にすると、基本型の動作 (または動作の変更) が非常に簡単にわかりにくくなります。方程式の両側を所有している場合、または「base.xxx」の呼び出しがサブメンバーの一部である場合、これは明らかに問題ではありません。

  • 基本/サブクラスの方程式の両方を実際に所有している場合は、制度化された隠蔽/シャドーイングよりも扱いやすいソリューションを考案できるはずです.
于 2008-09-20T03:13:36.533 に答える
1

これを実行したいコードベースがある場合、それは最適に設計されたコードベースではありません。これは通常、階層のあるレベルのクラスが特定の公開署名を必要としているのに対し、そのクラスから派生した別のクラスはそれを必要としないという兆候です。

今後のコーディング パラダイムは、「継承よりも構成」と呼ばれます。これは、オブジェクト指向開発の原則 (特に、単一責任の原則とオープン/クローズドの原則) を直接利用しています。

残念なことに、私たち開発者の多くがオブジェクト指向を教えられた方法で、合成ではなく継承についてすぐに考える習慣ができてしまいました。同じ「実世界」オブジェクトに含まれている可能性があるという理由だけで、多くの異なる責任を持つ大きなクラスを持つ傾向があります。これにより、クラス階層が 5 レベル以上の深さになる可能性があります。

開発者が継承を扱うときに通常考えない不幸な副作用は、継承が、コードに導入できる最も強力な依存関係の 1 つを形成することです。派生クラスは、継承元のクラスに強く依存しています。これにより、長期的にはコードが脆弱になり、基本クラスの特定の動作を変更すると、派生クラスがあいまいな方法で壊れるという交絡問題が発生する可能性があります。

コードを分割する 1 つの方法は、別の回答で言及されているようなインターフェイスを使用することです。とにかく、クラスの外部依存関係を具象/派生型ではなく抽象化にバインドする必要があるため、これは賢明なことです。これにより、依存クラスのコード行に影響を与えることなく、インターフェースを変更せずに実装を変更できます。

私は、ポリモーフィズム/継承を多用し、より緊密に結合されたクラスが少ないシステムを扱うよりも、数百/数千/さらに多くのクラスをすべて小さく疎結合したシステムを維持するよりもはるかに望んでいます。

おそらく、オブジェクト指向開発に関する最高のリソースは、Robert C. Martin の著書Agile Software Development, Principles, Patterns, and Practicesです。

于 2008-09-20T04:54:10.147 に答える
0

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.

Edit: Jorge Ferreira is correct.

于 2008-09-19T23:31:03.697 に答える