Warum sollte ich jemals C # geschachtelte classn verwenden müssen

Ich versuche, verschachtelte classn in C # zu verstehen. Ich verstehe, dass eine verschachtelte class eine class ist, die in einer anderen class definiert ist. Was ich nicht verstehe, ist, warum ich das jemals tun müsste.

Ein Muster, das ich besonders mag, ist es, verschachtelte classn mit dem Fabrikmuster zu kombinieren:

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() { ... } } 

Indem ich die classn so verschachtele, mache ich es Dritten unmöglich, eigene Unterklassen zu erstellen. Ich habe die vollständige Kontrolle über den gesamten Code, der in einem Bankkontenobjekt ausgeführt wird. Und alle meine Unterklassen können Implementierungsdetails über die Basisklasse teilen.

Der Zweck besteht normalerweise darin, den scope der geschachtelten class zu beschränken. Verschachtelte classn im Vergleich zu normalen classn haben die zusätzliche Möglichkeit des private Modifikators (und natürlich auch protected ).

Wenn Sie diese class nur innerhalb der class “parent” (in Bezug auf den Gültigkeitsbereich) verwenden müssen, ist es normalerweise sinnvoll, sie als geschachtelte class zu definieren. Wenn diese class möglicherweise ohne die Assembly / Bibliothek verwendet werden muss , ist es für den Benutzer normalerweise bequemer, sie als separate (Geschwister-) class zu definieren, unabhängig davon, ob eine konzeptionelle Beziehung zwischen den beiden classn besteht. Obwohl es technisch möglich ist, eine public class zu erstellen, die in einer public Elternklasse verschachtelt ist, ist dies meiner Meinung nach selten eine geeignete Implementierung.

Eine geschachtelte class kann private , protected und protected internal Zugriffsmodifizierer sowie public und internal Modifikatoren enthalten.

Beispielsweise implementieren Sie die GetEnumerator() -Methode, die ein IEnumerator -Objekt zurückgibt. Die Verbraucher würden sich nicht um den tatsächlichen Typ des Objekts kümmern. Sie wissen nur, dass es diese Schnittstelle implementiert. Die class, die Sie zurückgeben möchten, hat keine direkte Verwendung. Sie können diese class als private verschachtelte class deklarieren und eine Instanz davon zurückgeben (so implementiert der C # -Compiler Iteratoren):

 class MyUselessList : IEnumerable { // ... private List internalList; private class UselessListEnumerator : IEnumerator { 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 GetEnumerator() { return new UselessListEnumerator(this); } } 

Was ich nicht verstehe, ist, warum ich das jemals tun müsste

Ich denke, du musst das nie tun. Bei einer geschachtelten class wie dieser …

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

… Sie können die innere / geschachtelte class immer in den globalen Bereich verschieben, so …

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

Wenn B jedoch nur zum Implementieren von A verwendet wird, hat das Erstellen einer inneren / verschachtelten class zwei Vorteile:

  • Es verschmutzt nicht den globalen Geltungsbereich (zB Client-Code, der sieht, dass A nicht weiß, dass die B-class überhaupt existiert)
  • Die Methoden von B haben implizit Zugriff auf private Mitglieder von A; Wenn B nicht in A verschachtelt wäre, wäre B nicht in der Lage, auf Mitglieder von A zuzugreifen, außer wenn diese Mitglieder intern oder öffentlich wären. Aber diese Mitglieder intern oder öffentlich zu machen, würde sie auch anderen classn zugänglich machen (nicht nur B); Behalten Sie stattdessen diese Methoden von A privat und lassen Sie B darauf zugreifen, indem Sie B als geschachtelte class deklarieren. Wenn Sie C ++ kennen, ist das so, als würden Sie sagen, dass in C # alle verschachtelten classn automatisch ein ” Freund ” der class sind, in der sie enthalten sind (und das Deklarieren einer class als verschachtelt ist die einzige Möglichkeit, Freundschaft in C # zu deklarieren C # hat kein friend Keyword.

Wenn ich sage, dass B auf private Mitglieder von A zugreifen kann, nimmt das an, dass B einen Verweis auf A hat; was es oft tut, da verschachtelte classn oft so deklariert werden …

 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... } 

… und aus einer Methode von A mit Code wie diesem konstruiert …

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

Sie können ein Beispiel in Mehrdads Antwort sehen.

Es gibt auch eine gute Verwendung von öffentlichen verschachtelten Mitgliedern …

Geschachtelte classn haben Zugriff auf die privaten Mitglieder der äußeren class. Ein Szenario, in dem dies der richtige Weg ist, wäre ein Comparer (dh die Implementierung der IComparer-Schnittstelle).

In diesem Beispiel hat FirstNameComparer Zugriff auf das private Element _firstName, was nicht der Fall wäre, wenn die class eine separate class wäre …

 public class Person { private string _firstName; private string _lastName; private DateTime _birthday; //... public class FirstNameComparer : IComparer { public int Compare(Person x, Person y) { return x._firstName.CompareTo(y._lastName); } } } 

Es gibt Zeiten, in denen es nützlich ist, eine Schnittstelle zu implementieren, die aus der class zurückgegeben wird, aber die Implementierung dieser Schnittstelle sollte vollständig von der Außenwelt verborgen sein.

Als ein Beispiel – vor der Hinzufügung von yield zu C # – bestand eine Möglichkeit zum Implementieren von Enumeratoren darin, die Implementierung des Enumerators als private class in eine Auflistung zu stellen. Dies würde einen einfachen Zugang zu den Mitgliedern der Sammlung ermöglichen, aber die Außenwelt würde nicht die Details sehen müssen, wie dies implementiert wird.

Geschachtelte classn sind sehr nützlich für die Implementierung interner Details, die nicht verfügbar gemacht werden sollten. Wenn Sie Reflector verwenden, um classn wie Dictionary oder Hashtable zu überprüfen, finden Sie einige Beispiele.

Vielleicht ist dies ein gutes Beispiel dafür, wann verschachtelte classn verwendet werden sollen.

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

Und:

 // REFACTORED class ImageCache { Settings mSettings; List mEntries; class Settings {} class Entry {} } 

PS: Ich habe nicht berücksichtigt, welche Zugriffsmodifikatoren angewendet werden sollten (privat, geschützt, öffentlich, intern)