C#のネストされたクラスについて理解しようとしています。ネストされたクラスは別のクラス内で定義されているクラスであることを理解していますが、私が取得できないのは、これを行う必要がある理由です。
私が特に気に入っているパターンは、ネストされたクラスをファクトリパターンと組み合わせることです。
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オブジェクトで実行されるすべてのコードを完全に制御できます。すべてのサブクラスは、基本クラスを介して実装の詳細を共有できます。
通常、目的は、ネストされたクラスのscopeを制限することだけです。通常のクラスと比較してネストされたクラスには、private
修飾子の追加の可能性があります(もちろんprotected
も同様です)。
基本的に、「スコープ」の観点から「親」クラス内からのみこのクラスを使用する必要がある場合は、通常、ネストされたクラスとして定義するのが適切です。このクラスをAssembly/libraryなしから使用する必要がある場合、通常は、概念的な関係があるかどうかに関係なく、別個の(兄弟)クラスとして定義する方がユーザーにとって便利です。 2つのクラス間。 public
親クラス内にネストされたpublic
クラスを作成することは技術的には可能ですが、これは実装するのに適切なことではないでしょう。
ネストされたクラスには、private
およびprotected
に加えて、public
、internal
および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);
}
}
私が得られないのは、私がこれをする必要がある理由です
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つの利点があります。
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の返信で例を見ることができます。
ネストされたパブリックメンバーの優れた用途もあります...
ネストされたクラスは、外部クラスのプライベートメンバーにアクセスできます。したがって、これが正しい方法であるシナリオは、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);
}
}
}
クラス内から返されるインターフェイスを実装すると便利な場合がありますが、そのインターフェイスの実装は外部から完全に隠される必要があります。
例として、C#にyieldを追加する前に、列挙子を実装する1つの方法は、列挙子の実装をコレクション内のプライベートクラスとして配置することでした。これにより、コレクションのメンバーに簡単にアクセスできますが、外部の世界では、この実装方法の詳細は必要ありません。
ネストされたクラスは、公開されるべきではない内部の詳細を実装するのに非常に役立ちます。 Reflectorを使用して、Dictionary <Tkey、TValue>やHashtableなどのクラスをチェックする場合、いくつかの例があります。
たぶん、これはネストされたクラスを使用するときの良い例ですか?
// 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:どのアクセス修飾子を適用すべきかを考慮していません(プライベート、保護、パブリック、内部)