ネストされたクラスを使用するタイミングと理由


30

オブジェクト指向プログラミングを使用すると、クラス(ネストされたクラス)内にクラスを作成することができますが、4年のコーディング経験でネストしたクラスを作成したことはありません。
ネストされたクラスは何に適していますか?

クラスは、ネストされている場合、プライベートとしてマークでき、そのクラスのすべてのプライベートメンバーに包含クラスからアクセスできることを知っています。変数を含むクラス自体に変数をプライベートとして置くことができます。
では、なぜネストされたクラスを作成するのでしょうか?

ネストされたクラスはどのシナリオで使用する必要がありますか、または他の手法よりも使用法の点でより強力ですか?


1
あなたにはいくつかの良い答えがあり、時には私はクラス内で必要な作業クラスまたはストラットだけを持っています。
パパラッチ

回答:


19

ネストされたクラスの主な機能は、クラス自体のフルパワーを持ちながら、外部クラスのプライベートメンバーにアクセスできることです。また、特定の状況で非常に強力なカプセル化を可能にするプライベートにすることもできます。

クラスはプライベートであるため、ここではセッターを完全にファクトリーにロックダウンします。これは、コンシューマーがクラスをダウンキャストしてセッターにアクセスできず、許可されるものを完全に制御できるためです。

public interface IFoo 
{
    int Foo{get;}      
}
public class Factory
{
    private class MyFoo : IFoo
    {
        public int Foo{get;set;}
    }
    public IFoo CreateFoo(int value) => new MyFoo{Foo = value};
}

それ以外は、プライベートメンバーにアクセスできる制御された環境でサードパーティのインターフェイスを実装するのに役立ちます。

たとえば、他のオブジェクトへのインターフェイスのインスタンスを提供するが、メインクラスにそれを実装させたくない場合、内部クラスに実装させることができます。

public class Outer
{
    private int _example;
    private class Inner : ISomeInterface
    {
        Outer _outer;
        public Inner(Outer outer){_outer = outer;}
        public int DoStuff() => _outer._example;
    }
    public void DoStuff(){_someDependency.DoBar(new Inner(this)); }
}

ほとんどの場合、2番目の例
ベンアーロンソン

@BenAaronsonデリゲートを使用してランダムインターフェイスをどのように実装しますか?
エスベンスコフペダーセン

@EsbenSkovPedersenさてあなたの例のために、代わりのインスタンスを渡すのでOuter、あなたは合格したいFunc<int>だけだろうこれは、() => _example
ベン・アーロンソン

@BenAaronsonはこの非常に単純なケースでは正しいですが、より複雑な例では、あまりにも多くのデリゲートが不器用になります。
エスベンスコフペダーセン

@EsbenSkovPedersen:あなたの例にはいくつかのメリットがありますが、IMOはInnerネストされinternalずに機能しない場合(つまり、異なるアセンブリを処理していない場合)にのみ使用してください。クラスをネストすると読みやすくなるため、internal(可能な場合)を使用するよりも不利になります。
フラット

23

通常、ネストされたクラスNは、CがCの外部で(直接)使用してはならないものを内部で使用する必要がある場合、および何らかの理由で既存のものではなく新しいタイプのオブジェクトである必要がある場合に、クラスCの内部に作成されますタイプ。

これは、ほとんどの場合、何らかのインターフェイスを実装するオブジェクトを返すメソッドを実装するときに発生し、他の場所では役に立たないため、そのオブジェクトの具体的なタイプを非表示にしたいと考えています。

IEnumerableの実装は、この良い例です。

class BlobOfBusinessData: IEnumerable<BusinessDatum>
{
    public IEnumerator<BusinessDatum> GetEnumerator()
    {
         return new BusinessDatumEnumerator(...);
    }

    class BusinessDatumEnumerator: IEnumerator<BusinessDatum>
    {
        ...
    }
}

外部の人BlobOfBusinessDataが具体的なBusinessDatumEnumerator型を知ったり気にしたりする理由はまったくないので、内部に保持することもできBlobOfBusinessDataます。

これはIEnumerable、適切に実装する方法の「ベストプラクティス」の例ではなく、アイデアを広めるための最低限のものであるため、明示的なIEnumerable.GetEnumerator()方法などを省略しました。


6
私が新しいプログラマーで使用する別の例は、のNodeクラスLinkedListです。を使用するユーザーは、コンテンツにアクセスできる限り、の実装LinkedList方法を気にしませんNode。気にする唯一のエンティティはLinkedListクラスそのものです。
メイジザイ

3

では、なぜネストされたクラスを作成するのでしょうか?

私はいくつかの重要な理由を考えることができます:

1.カプセル化を有効にする

多くの場合、ネストされたクラスは、クラスの実装の詳細です。メインクラスのユーザーは、自分の存在を気にする必要はありません。メインクラスのユーザーにコードの変更を要求することなく、自由に変更できる必要があります。

2.名前の汚染を避ける

型、変数、関数などをスコープに追加するのは適切ではありません。これはカプセル化とは少し異なります。ネストされた型のインターフェイスを公開すると便利ですが、ネストされた型の適切な場所は依然としてメインクラスです。C ++の土地では、イテレータ型がその一例です。私はあなたにC#で具体的な例を与えるのに十分なC#の経験がありません。

ネストされたクラスをメインクラスと同じスコープに移動することが名前の汚染である理由を説明するために、単純化した例を作成しましょう。リンクリストクラスを実装しているとします。通常、使用します

publid class LinkedList
{
   class Node { ... }
   // Use Node to implement the LinkedList class.
}

Nodeと同じスコープに移動することにしたLinkedList場合は、

public class LinkedListNode
{
}

public class LinkedList
{
  // Use LinkedListNode to implement the class
}

LinkedListNodeLinkedListクラス自体がなければ便利ではないでしょう。のユーザーが使用できるオブジェクトLinkedListを返す関数を提供した場合でも、使用された場合にのみ有用になります。そのため、「ノード」クラスをピアクラスにすると、包含スコープが汚染されます。LinkedListNodeLinkedListLinkedListNodeLinkedListLinkedList


0
  1. 関連するヘルパークラスには、ネストされたパブリッククラスを使用します。

    public class MyRecord {
        // stuff
        public class Comparer : IComparer<MyRecord> {
        }
        public class EqualsComparer : IEqualsComparer<MyRecord> {
        }
    }
    MyRecord[] array;
    Arrays.sort(array, new MyRecord.Comparer());
    
  2. 関連するバリエーションに使用します。

    // Class that may or may not be mutable.
    public class MyRecord {
        protected string name;
        public virtual String Name { get => name; set => throw new InvalidOperation(); }
    
        public Mutable {
            public override String { get => name; set => name = value; }
        }
    }
    
    MyRecord mutableRecord = new MyRecord.Mutable();
    

発信者は、どのバージョンがどの問題に適しているかを選択できます。クラスは1回のパスで完全に構築できない場合があり、変更可能なバージョンが必要です。これは、循環データを処理する場合に常に当てはまります。ミュータブルは、後で読み取り専用に変換できます。

  1. 内部記録に使用します

    public class MyClass {
        List<Line> lines = new List<Line>();
    
        public void AddUser( string name, string address ) => lines.Add(new Line { Name = name, Address = address });
    
        class Line { string Name; string Address; }
    }
    

0

ネストされたクラスは、クラスのインスタンスを複数回作成する場合、またはそのタイプをより利用可能にする場合に使用できます。

ネストされたクラスはカプセル化を増やし、コードをより読みやすく保守しやすくします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.