web-dev-qa-db-ja.com

なぜC#のネストされたクラスを使用する必要があるのか

C#のネストされたクラスについて理解しようとしています。ネストされたクラスは別のクラス内で定義されているクラスであることを理解していますが、私が取得できないのは、これを行う必要がある理由です。

212
Dan Norton

私が特に気に入っているパターンは、ネストされたクラスをファクトリパターンと組み合わせることです。

public abstract class BankAccount
{
  private BankAccount() {} // prevent third-party subclassing.
  private sealed class SavingsAccount : BankAccount { ... }
  private sealed class ChequingAccount : BankAccount { ... }
  public static BankAccount MakeSavingAccount() { ... }
  public static BankAccount MakeChequingAccount() { ... }
}

このようにクラスをネストすることにより、第三者が独自のサブクラスを作成することを不可能にします。任意のbankaccountオブジェクトで実行されるすべてのコードを完全に制御できます。すべてのサブクラスは、基本クラスを介して実装の詳細を共有できます。

255
Eric Lippert

通常、目的は、ネストされたクラスのscopeを制限することだけです。通常のクラスと比較してネストされたクラスには、private修飾子の追加の可能性があります(もちろんprotectedも同様です)。

基本的に、「スコープ」の観点から「親」クラス内からのみこのクラスを使用する必要がある場合は、通常、ネストされたクラスとして定義するのが適切です。このクラスをAssembly/libraryなしから使用する必要がある場合、通常は、概念的な関係があるかどうかに関係なく、別個の(兄弟)クラスとして定義する方がユーザーにとって便利です。 2つのクラス間。 public親クラス内にネストされたpublicクラスを作成することは技術的には可能ですが、これは実装するのに適切なことではないでしょう。

109
Noldorin

ネストされたクラスには、privateおよびprotectedに加えて、publicinternalおよびprotected internalアクセス修飾子を含めることができます。

たとえば、IEnumerator<T>オブジェクトを返すGetEnumerator()メソッドを実装しています。消費者は、オブジェクトの実際のタイプを気にしません。彼らが知っているのは、それがそのインターフェースを実装していることだけです。返すクラスは直接使用しません。そのクラスをprivateネストされたクラスとして宣言し、そのインスタンスを返すことができます(これは実際にはC#コンパイラがイテレータを実装する方法です)。

class MyUselessList : IEnumerable<int> {
    // ...
    private List<int> internalList;
    private class UselessListEnumerator : IEnumerator<int> {
        private MyUselessList obj;
        public UselessListEnumerator(MyUselessList o) {
           obj = o;
        }
        private int currentIndex = -1;
        public int Current {
           get { return obj.internalList[currentIndex]; }
        }
        public bool MoveNext() { 
           return ++currentIndex < obj.internalList.Count;
        }
    }
    public IEnumerator<int> GetEnumerator() {
        return new UselessListEnumerator(this);
    }
}
50
Mehrdad Afshari

私が得られないのは、私がこれをする必要がある理由です

needこれを行うことは決してないと思います。このようなネストされたクラスを考えると...

class A
{
  //B is used to help implement A
  class B
  {
    ...etc...
  }
  ...etc...
}

...次のように、常に内部/ネストされたクラスをグローバルスコープに移動できます...

class A
{
  ...etc...
}

//B is used to help implement A
class B
{
  ...etc...
}

ただし、BがAの実装を支援するためだけに使用される場合、Bを内部/ネストクラスにすることには2つの利点があります。

  • グローバルスコープを汚染しません(たとえば、AがBクラスが存在することを認識できないクライアントコード)
  • Bのメソッドは、Aのプライベートメンバーに暗黙的にアクセスします。一方、BがA内にネストされていない場合、BはAのメンバーが内部またはパブリックでない限りアクセスできません。しかし、それらのメンバーを内部またはパブリックにすることで、メンバーを他のクラス(Bだけでなく)に公開することになります。その代わりに、Aのメソッドをプライベートにして、Bをネストされたクラスとして宣言することでBにアクセスできるようにします。 C++を知っている場合、これは、C#ではすべてのネストされたクラスは自動的にそれらが含まれるクラスの 'friend'であると言うことに似ています(そして、C#にはfriendキーワードがないため、クラスをネストされていると宣言することがC#で友情を宣言する唯一の方法です。

BがAのプライベートメンバーにアクセスできると言うとき、それはBがAへの参照を持っていると仮定しています。ネストされたクラスは多くの場合このように宣言されるため...

class A
{
  //used to help implement A
  class B
  {
    A m_a;
    internal B(A a) { m_a = a; }
    ...methods of B can access private members of the m_a instance...
  }
  ...etc...
}

...そして、このようなコードを使用してAのメソッドから構築されます...

//create an instance of B, whose implementation can access members of self
B b = new B(this);

Mehrdadの返信で例を見ることができます。

43
ChrisW

ネストされたパブリックメンバーの優れた用途もあります...

ネストされたクラスは、外部クラスのプライベートメンバーにアクセスできます。したがって、これが正しい方法であるシナリオは、Comparerを作成するとき(つまり、IComparerインターフェイスを実装するとき)になります。

この例では、FirstNameComparerはプライベート_firstNameメンバーにアクセスできますが、クラスが別のクラスである場合はアクセスできません...

public class Person
{
    private string _firstName;
    private string _lastName;
    private DateTime _birthday;

    //...

    public class FirstNameComparer : IComparer<Person>
    {
        public int Compare(Person x, Person y)
        {
            return x._firstName.CompareTo(y._lastName);
        }
    }

}
29
Arjan Einbu

クラス内から返されるインターフェイスを実装すると便利な場合がありますが、そのインターフェイスの実装は外部から完全に隠される必要があります。

例として、C#にyieldを追加する前に、列挙子を実装する1つの方法は、列挙子の実装をコレクション内のプライベートクラスとして配置することでした。これにより、コレクションのメンバーに簡単にアクセスできますが、外部の世界では、この実装方法の詳細は必要ありません。

18
Reed Copsey

ネストされたクラスは、公開されるべきではない内部の詳細を実装するのに非常に役立ちます。 Reflectorを使用して、Dictionary <Tkey、TValue>やHashtableなどのクラスをチェックする場合、いくつかの例があります。

5
Fernando

たぶん、これはネストされたクラスを使用するときの良い例ですか?

// ORIGINAL
class ImageCacheSettings { }
class ImageCacheEntry { }
class ImageCache
{
    ImageCacheSettings mSettings;
    List<ImageCacheEntry> mEntries;
}

そして:

// REFACTORED
class ImageCache
{
    Settings mSettings;
    List<Entry> mEntries;

    class Settings {}
    class Entry {}
}

PS:どのアクセス修飾子を適用すべきかを考慮していません(プライベート、保護、パブリック、内部)

3
lambertwm