25

このstackoverflowの回答では、コメント投稿者が「プライベートネストされたクラス」は非常に役立つ可能性があると述べたので、ネストされたクラスが技術的にどのように機能するかを説明する傾向があるこのような記事でそれらについて読んでいましたが、それらを使用する理由は説明していません。

より大きなクラスに属する小さなヘルパー クラスにはプライベート ネスト クラスを使用すると思いますが、多くの場合、別のクラスのヘルパー クラスが必要になるため、(1) ネストされたクラスを非-nested または (2) public にしてから、outer-class プレフィックスを付けてアクセスします。これはどちらも、最初にネストされたクラスを持つための付加価値のない余分な作業のようです。したがって、一般的に、おそらくクラスをグループにもう少し整理する以外に、ネストされたクラスのユースケースは実際には見られませんが、それは私が楽しむようになったファイルごとに1つのクラスという明確さに反します.

コードをより管理しやすく、読みやすく、効率的にするために、ネストされたクラスをどのように使用しますか?

4

13 に答える 13

31

あなたはあなた自身の質問に答えました。クラス外では意味のないヘルパー クラスが必要な場合は、ネストされたクラスを使用します。特に、ネストされたクラスが外部クラスのプライベート実装の詳細を利用できる場合。

ネストされたクラスが役に立たないというあなたの主張は、プライベート メソッドが役に立たないという主張でもあります。プライベート メソッドはクラスの外で役立つ可能性があるため、内部にする必要があります。内部メソッドはアセンブリの外で役立つ場合があるため、公開します。したがって、すべてのメソッドは公開する必要があります。それが悪い議論だと思うなら、メソッドの代わりにクラスに対して同じ議論をすることの違いは何ですか?

クラスの外では意味をなさないヘルパーに機能をカプセル化する必要があり、外部クラスのプライベート実装の詳細を使用できることがよくあるため、常にネストされたクラスを作成します。たとえば、私はコンパイラを書きます。私は最近、解析ツリーのセマンティック分析を行うクラス SemanticAnalyzer を作成しました。ネストされたクラスの 1 つが LocalScopeBuilder です。私がそうでないときに、どのような状況でローカルスコープを構築する必要がありますか解析木のセマンティクスを分析しますか? 一度もない。そのクラスは、完全にセマンティック アナライザーの実装の詳細です。クラス外では役に立たない NullableArithmeticAnalyzer や OverloadResolutionAnalyzer などの名前を持つネストされたクラスをさらに追加する予定ですが、言語のルールをそれらの特定のクラスにカプセル化したいと考えています。

また、ネストされたクラスを使用して、イテレータやコンパレータなどを構築します。これは、クラスの外では意味がなく、よく知られているインターフェイスを介して公開されます。

私が非常に頻繁に使用するパターンは、外側のクラスを拡張するネストされたプライベート クラスを持つことです。

abstract public class BankAccount
{
    private BankAccount() { }
    // Now no one else can extend BankAccount because a derived class
    // must be able to call a constructor, but all the constructors are
    // private!
    private sealed class ChequingAccount : BankAccount { ... }
    public static BankAccount MakeChequingAccount() { return new ChequingAccount(); }
    private sealed class SavingsAccount : BankAccount { ... }

等々。ネストされたクラスは、ファクトリ パターンで非常にうまく機能します。ここで BankAccount は、さまざまな種類の銀行口座のファクトリであり、そのすべてが BankAccount のプライベート実装の詳細を使用できます。しかし、第三者が BankAccount を拡張した独自の EvilBankAccount 型を作成することはできません。

于 2010-07-21T14:11:27.343 に答える
11

非表示にする実装の呼び出し元にインターフェイスを返します。

public class Outer
{
    private class Inner : IEnumerable<Foo>
    {
        /* Presumably this class contains some functionality which Outer needs
         * to access, but which shouldn't be visible to callers 
         */
    }

    public IEnumerable<Foo> GetFoos()
    {
        return new Inner();
    }
}
于 2010-07-21T14:07:50.360 に答える
7

プライベート ヘルパー クラスが良い例です。

たとえば、バックグラウンド スレッドの状態オブジェクトです。これらの型を公開するやむを得ない理由はありません。それらをプライベートなネストされた型として定義することは、ケースを処理するための非常にクリーンな方法のようです。

于 2010-07-21T14:03:49.060 に答える
6

2 つのバインドされた値 (ハッシュ テーブルなど) が内部的には十分ではなく、外部的には十分である場合に使用します。次に、格納する必要があるプロパティを含む入れ子になったクラスを作成し、メソッドを介してそれらのいくつかのみを公開します。

これは理にかなっていると思います。他の誰もそれを使用しないのであれば、なぜ外部クラスを作成するのでしょうか? 意味がありません。

ファイルごとに1つのクラスについては、partialキーワードを使用して部分クラスを作成できます。これは私が通常行うことです。

于 2010-07-21T14:04:24.733 に答える
6

私が最近遭遇した 1 つの説得力のある例は、Node多くのデータ構造のクラスです。Quadtreeたとえば、Aはノードにデータを格納する方法を知る必要がありますが、コードの他の部分は気にする必要はありません。

于 2010-07-21T14:08:54.893 に答える
5

非常に便利なケースをいくつか見つけました。

  1. Interpolator クラスで使用される InterpolationTriangle などの複雑なプライベート ステートの管理。Interpolator のユーザーは、それが Delauney 三角形分割を使用して実装されていることを知る必要はなく、三角形について知る必要もありません。そのため、データ構造はネストされたプライベート クラスです。

  2. 他の人が述べたように、クラスの完全な実装を明らかにすることなく、クラスで使用されるデータをインターフェイスで公開できます。ネストされたクラスは、外部クラスのプライベート状態にもアクセスできます。これにより、密結合を公開することなく (またはアセンブリの残りの部分に内部的にさらすことさえ)、密結合コードを記述できます。

  3. フレームワークがクラ​​スが何らかの基本クラス (WPF の DependencyObject など) から派生することを期待しているが、別の基本クラスからクラスを継承する必要があるといういくつかのケースに遭遇しました。フレームワークの基本クラスから派生したプライベート ネスト クラスを使用して、フレームワークと相互運用することができます。ネストされたクラスはプライベートな状態にアクセスできるため (作成時に親の「this」を渡すだけです)、基本的に this を使用して、コンポジションを介して貧弱な人の多重継承を実装できます。

于 2010-07-21T14:17:23.593 に答える
3

この手法が使用される非常に一般的なパターンの 1 つは、クラスがそのプロパティまたはメソッドの 1 つからインターフェイスまたは基本クラスの型を返すが、具象型がプライベートの入れ子になったクラスであるというシナリオです。次の例を考えてみましょう。

public class MyCollection : IEnumerable
{
  public IEnumerator GetEnumerator()
  {
    return new MyEnumerator();
  }

  private class MyEnumerator
  {
  }
}
于 2010-07-21T14:08:19.073 に答える
3

パブリックおよびプライベートのネストされたクラスのユースケースについては、他の人がうまくカバーしていると思います。

one-class-per-fileに関するあなたの懸念への回答は、私が見ていないことの 1 つです。これを解決するには、外部クラスを部分的に作成し、内部クラスの定義を別のファイルに移動します。

OuterClass.cs:

namespace MyNameSpace
{
    public partial class OuterClass
    {
        // main class members here
        // can use inner class
    }
}

OuterClass.Inner.cs:

namespace MyNameSpace
{
    public partial class OuterClass
    {
        private class Inner
        {
            // inner class members here
        }
    }
}

Visual Studio の項目のネストを利用して、OuterClass.Inner.cs を OuterClass.cs の「子」にして、ソリューション エクスプローラーが煩雑にならないようにすることもできます。

于 2010-07-21T21:30:47.557 に答える
2

私は通常、特定の状況で SRP (Single Responsibility Principal) の組み合わせが必要な場合に行います。

「SRP が目標なら、それらを別のクラスに分割してみませんか?」 80% の確率でこれを行いますが、作成したクラスが外部の世界にとって役に立たない状況はどうでしょうか? アセンブリの API を乱雑にするために自分だけが使用するクラスは必要ありません。

「まあ、そのinternalためじゃない?」 もちろん。これらのケースの約80%。しかし、パブリック クラスの状態にアクセスしたり変更したりする必要がある内部クラスについてはどうでしょうか。たとえば、SRP ストリークを満たすために 1 つ以上の内部クラスに分割されたクラスは? 同様に、これらのinternalクラスで使用するすべてのメソッドとプロパティをマークする必要があります。internal

"それの何がいけないの?" 何もない。これらのケースの約80%。もちろん、以前に作成したクラスにのみ使用されるメソッド/プロパティを使用して、クラスの内部インターフェイスを乱雑にしています。そして今、あなたのチームの他の人が内部コードを書いて、あなたが予期していなかった方法でそれらのメソッドを使用してあなたの状態を台無しにしないか心配しなければなりません.

内部クラスは、それらが定義されている型のインスタンスの状態を変更できます。そのため、型の定義にメンバーを追加しなくても、必要に応じて内部クラスで作業できます。これは、100 件中約 14 件のケースで、型をクリーンに保ち、コードの信頼性と保守性を維持し、責任を単一化するための最善の策です。

于 2010-07-21T15:47:27.977 に答える
1

例として、シングルトン パターンの実装に非常に適しています。

値を「追加」するためにそれらを使用している場所もいくつかあります。内部クラスがチェックボックスの状態とデータ項目も保存する複数選択コンボボックスがあります。世界がこの内部クラスについて知る/使用する必要はありません。

于 2010-07-21T14:05:30.283 に答える
0

オブジェクトがその状態に関する抽象情報を返す必要がある場合は、入れ子になったプライベート クラスが適している場合があります。たとえば、Fnord が「コンテキストの保存」および「コンテキストの復元」メソッドをサポートしている場合、「コンテキストの保存」関数がタイプ Fnord.SavedContext のオブジェクトを返すようにすると便利な場合があります。型アクセス ルールが常に最も役立つとは限りません。たとえば、Fnord が Fnord.SavedContext のプロパティやメソッドにアクセスできるようにすることは、そのようなプロパティやメソッドを部外者に公開せずに許可するのは難しいようです。一方、Fnord.CreateSaveContext で Fnord をパラメータとして新しい Fnord.SaveContext を作成するだけで (Fnord.SaveContext は Fnord の内部にアクセスできるため)、Fnord.LoadContextFrom() で Fnord.SaveContext.RestoreContextTo を呼び出すことができます。 ().

于 2010-07-21T15:56:39.000 に答える
0

GUI のイベント ハンドラーには、ネストされた非公開の匿名クラスが不可欠です。

一部のクラスが、別のクラスがエクスポートする API の一部ではない場合、非公開にする必要があります。そうしないと、意図した以上に公開されます。「百万ドルのバグ」がその例です。ほとんどのプログラマーは、これについてあまりにも怠惰です。

ピーター

于 2010-07-21T14:12:42.630 に答える
0

質問にはC#のタグが付けられているので、これが興味深いかどうかはわかりませんが、COMでは、クラスC ++が複数のCOMインターフェイスを実装する場合、内部クラスを使用してインターフェイスを実装できます...基本的に、複数の継承ではなく構成に使用します。

さらに、MFC やその他のテクノロジでは、コントロール/ダイアログにドロップ ターゲット クラスが必要になる場合がありますが、これは入れ子になったクラス以外ではほとんど意味がありません。

于 2010-07-21T14:59:46.887 に答える