Perché dovrei mai aver bisogno di usare le classi nidificate in C #

Sto cercando di capire le classi nidificate in C #. Capisco che una class annidata è una class che è definita all’interno di un’altra class, ciò che non capisco è il motivo per cui avrei mai avuto bisogno di farlo.

    Un pattern che mi piace particolarmente è quello di combinare le classi annidate con il modello factory:

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

    Con l’annidamento di classi come questa, per le terze parti è imansible creare le proprie sottoclassi. Ho il controllo completo su tutto il codice che viene eseguito in qualsiasi object bankaccount. E tutte le mie sottoclassi possono condividere i dettagli di implementazione tramite la class base.

    Lo scopo è in genere solo per limitare l’ ambito della class nidificata. Le classi annidate rispetto alle classi normali hanno l’ulteriore possibilità del modificatore private (oltre che ovviamente protected ).

    Fondamentalmente, se hai solo bisogno di usare questa class all’interno della class “parent” (in termini di scope), allora di solito è appropriato definirla come una class nidificata. Se è ansible che questa class debba essere utilizzata senza l’assembly o la libreria , in genere è più conveniente per l’utente definirla come class separata (di pari livello), indipendentemente dal fatto che esista o meno una relazione concettuale tra le due classi. Anche se è tecnicamente ansible creare una class public nidificata all’interno di una class genitore public , questa è, a mio avviso, raramente una cosa appropriata da implementare.

    Una class nidificata può avere modificatori di accesso protected internal private , protected e protected internal insieme a public e internal .

    Ad esempio, si sta implementando il metodo GetEnumerator() che restituisce un object IEnumerator . I consumatori non si preoccuperebbero del tipo effettivo dell’object. Tutto ciò che sanno è che implementa quell’interfaccia. La class che vuoi restituire non ha alcun uso diretto. Puoi dichiarare quella class come una class nidificata private e restituirne un’istanza (questo è in realtà il modo in cui il compilatore C # implementa gli iteratori):

     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); } } 

    quello che non capisco è perché mai avrei bisogno di farlo

    Penso che tu non abbia mai bisogno di farlo. Dato una class nidificata come questa …

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

    … puoi sempre spostare la class interna / nidificata in ambito globale, come questo …

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

    Tuttavia, quando B è usato solo per aiutare a implementare A, quindi rendere B una class interna / nidificata ha due vantaggi:

    • Non inquina l’ambito globale (ad esempio il codice client che può vedere A non sa che la class B esiste anche)
    • I metodi di B hanno implicitamente accesso ai membri privati ​​di A; mentre se B non fosse annidato all’interno di A, B non sarebbe in grado di accedere ai membri di A a meno che tali membri fossero interni o pubblici; ma poi rendere tali membri interni o pubblici li esporrà anche ad altre classi (non solo B); quindi, invece, mantieni i metodi di A privati ​​e lascia che B li acceda dichiarando B come una class annidata. Se conosci C ++, è come dire che in C # tutte le classi annidate sono automaticamente un ‘ amico ‘ della class in cui sono contenute (e, che dichiarare una class come nidificata è l’unico modo per dichiarare amicizia in C #, poiché C # non ha una parola chiave friend ).

    Quando dico che B può accedere ai membri privati ​​di A, supponiamo che B abbia un riferimento ad A; che spesso fa, dal momento che le classi annidate sono spesso dichiarate in questo modo …

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

    … e costruito con un metodo di A usando un codice come questo …

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

    Puoi vedere un esempio nella risposta di Mehrdad.

    Ci sono anche buoni usi dei membri nidificati pubblici …

    Le classi nidificate hanno accesso ai membri privati ​​della class esterna. Quindi uno scenario in cui questo è il modo giusto sarebbe quando si crea un Comparer (cioè l’implementazione dell’interfaccia IComparer).

    In questo esempio, FirstNameComparer ha accesso al membro _firstName privato, che non sarebbe se la class fosse una class separata …

     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); } } } 

    Ci sono momentjs in cui è utile implementare un’interfaccia che verrà restituita all’interno della class, ma l’implementazione di tale interfaccia dovrebbe essere completamente nascosta al mondo esterno.

    Ad esempio, prima dell’aggiunta di yield a C #, un modo per implementare gli enumeratori era di mettere l’implementazione dell’enumeratore come una class privata all’interno di una raccolta. Ciò fornirebbe un facile accesso ai membri della collezione, ma il mondo esterno non avrebbe bisogno / vedere i dettagli di come questo è implementato.

    Le classi annidate sono molto utili per l’implementazione di dettagli interni che non dovrebbero essere esposti. Se usi Reflector per controllare classi come Dictionary o Hashtable troverai alcuni esempi.

    Forse questo è un buon esempio di quando usare le classi annidate?

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

    E:

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

    PS: non ho preso in considerazione quali modificatori di accesso dovrebbero essere applicati (privato, protetto, pubblico, interno)