2009-03-26 9 views
45

Sto lavorando su un codice precedente e ho trovato qualcosa di cui non sono sicuro. Abbiamo un class y dichiarato all'interno di un altro class x. Class y viene utilizzato sempre solo all'interno di class x ma la mia domanda è: perché non dovresti creare un file di classe separato e inserire class y lì invece di dichiararlo all'interno di class x? Non è questo a violare le OOP o è solo una questione di stile dato che è sempre e solo usato all'interno di questa classe. Sto refactoring alcuni di questo codice e la mia prima reazione sarebbe quella di separare class y nel proprio file.
Classe dichiarata all'interno di un'altra classe in C#

namespace Library 
{ 
    public class x 
    { 
     // methods, properties, local members of class x 

     class y 
     { 
     // methods, properties, local members of class y 
     } 
    } 
} 

risposta

58

si crea una classe interna, perché è sempre e solo utilizzato nell'ambito di Classe X e si inserisce logicamente nel factoring/architettura della classe di x.

La classe y potrebbe anche essere al corrente dei dettagli di implementazione della classe x che non sono destinati ad essere noti al pubblico.

+0

È vero? Dai miei test, la classe Y non può accedere ai dettagli della classe X. Come sarebbe possibile? – Nitax

+2

Nevermind, appena trovato http://blogs.msdn.com/b/oldnewthing/archive/2006/08/01/685248.aspx – Nitax

19

Questo ha implicazioni sui permessi. Una "classe y" di primo livello sarebbe "interna" - tuttavia, qui "y" è privato di "x". Questo approccio è utile per i dettagli di implementazione (ad esempio le righe della cache ecc.). Allo stesso modo, ha accesso a tutto lo stato privato di x.

Ci sono anche implicazioni con i generici; x<T>.y è generico "di T", ereditato dalla classe esterna. Puoi visualizzarlo qui, dove Bar ha pieno utilizzo di T e nota che tutti i campi statici di Bar hanno un ambito per- T.

class Foo<T> { 
    void Test(T value) { 
     Bar bar = new Bar(); 
     bar.Value = value; 
    } 
    class Bar { 
     public T Value { get; set; } 
    } 
} 

Spesso la gente pensa erroneamente di cui hanno bisogno per definire Bar come Bar<T> - questo è ora (efficace) doppiamente generico - vale a dire Foo<TOld, T> - dove TOld è la (ormai introvabili) T da Foo<T>. Quindi non farlo! Oppure se hai vuoi per essere doppiamente generico, scegli nomi diversi. Fortunatamente, il compilatore ti avverte di questo ...

+3

+1 Potrebbe essere una buona idea spiegare come "T" della classe esterna sarà essere parte del tipo chiuso costruito della classe interiore. È un punto importante che molti non conoscono. –

+0

Devi inoltrare la dichiarazione "Barra" prima di usarla nella definizione di "Test()'? –

2

Penso che sia ok, purché la classe contenuta sia usata solo come utility. Io uso questo tipo di costrutto, ad esempio, per definire tipi di ritorno complessi per metodi privati.

2

Ho appena passato il codice che sto aggiornando (e ho originariamente scritto) e rimosso tutte le classi annidate. Sfortunatamente, ho originariamente utilizzato la classe nidificata al di fuori della classe in cui è stata definita. Spostare le classi nidificate mi ha fatto un'enorme differenza perché inizialmente avevo un design scadente.

se Y viene utilizzato solo in X e non sarà mai utilizzato al di fuori di X, direi tenerlo lì

7

Questo codice va bene per l'esatto motivo che avete dato - "classe y è sempre e solo usato all'interno della classe x ". Questi sono nested types e una delle linee guida per il loro utilizzo è che i tipi annidati dovrebbero essere strettamente accoppiati al loro tipo di dichiarazione e non devono essere utili come tipo generico. In questo modo la classe annidata è inaccessibile ad altre classi, ma consente comunque di seguire i principi orientati agli oggetti.

2

Lasciatemi fare un esempio dell'uso di classi nidificate che potrebbero chiarire quando questo tipo di architettura è appropriato. Di recente avevo bisogno di generare una tabella HTML estraendo colonne selezionate da una tabella di dati e "ruotandole" in modo che le righe diventassero colonne e viceversa.Nel mio caso, ci sono state due operazioni fondamentali: la rotazione dei dati e la generazione di un output piuttosto complesso (non stavo solo mostrando i dati: ogni colonna di dati/riga della tabella era soggetta a operazioni per estrarre il titolo, generare tag immagine, impostare link, ecc. quindi usare un pivot SQL non aveva proprio ragione).

Dopo un tentativo iniziale di creare una classe per eseguire l'intera operazione, ho riconosciuto che molti dei dati/metodi sono stati suddivisi in tre partizioni distinte: elaborazione intestazione, elaborazione riga e pivot. Così, ho deciso che un approccio migliore sarebbe stato quello di incapsulare la logica per "intestazione" e "riga" in classi separate e annidate. Ciò mi ha permesso di separare i dati detenuti da ciascuna riga e programmare le operazioni di pivot in modo molto pulito (chiamando un oggetto riga separato per ogni colonna nella tabella dati). Alla fine delle operazioni pivot, ho generato l'output chiamando l'oggetto header e quindi ogni oggetto row a sua volta per generare il suo output di nuovo alla classe principale.

Le classi separate non erano appropriate perché A) le classi nidificate avevano bisogno di alcuni dati dalla classe master e B) l'elaborazione era molto specifica e non utile altrove. La semplice programmazione di una grande classe era semplicemente disordinata a causa della confusione che circonda termini come "colonna" e "riga" che differivano a seconda che si stesse parlando di dati o di output HTML. Inoltre, questo era un lavoro insolito in quanto stavo generando HTML nella mia classe di business, quindi volevo separare la pura business logic dalla generazione dell'interfaccia utente. Alla fine, le classi nidificate hanno fornito il perfetto equilibrio, quindi, di incapsulamento e condivisione dei dati.

1

Si può ancora effettuare il refactoring della classe in un altro file, ma utilizzare una classe parial. Il vantaggio di questo è che hai ancora una classe per file e non hai i problemi di refactoring di spostare la dichiarazione al di fuori della classe x.

ad es. potresti avere un file di codice: x.y.cs che somiglierebbe a

partial class X 
{ 
    class Y 
    { 
     //implementation goes here 
    } 
} 
Problemi correlati