Una classe per regola file in .NET? [chiuso]


185

Seguo questa regola ma alcuni dei miei colleghi non sono d'accordo con essa e sostengono che se una classe è più piccola può essere lasciata nello stesso file con altre classi.

Un altro argomento che sento sempre è "Anche Microsoft non lo fa, quindi perché dovremmo?"

Qual è il consenso generale su questo? Ci sono casi in cui questo dovrebbe essere evitato?


1
possibile duplicato di classi C # in file separati?

Risposte:


176

Una classe per file ti dà anche una migliore idea di cosa sta cambiando ogni check in senza guardare le differenze del file.


4
Più classi nello stesso file aumentano la differenza tra le classi correlate in una sola operazione.
Luca

3
I visualizzatori diff appropriati ti consentono di visualizzare tutte le differenze di un commit.
Dykam,

44
Non sono del tutto sicuro di come questa particolare risposta sia LA risposta alle domande del post originale. Certamente fornisce un motivo per mantenere 1 classe in un file (anche se Luca e Dykam danno buoni punti), ma non riflette il consenso generale o fornisce casi in cui dovrebbe essere evitato.
Robert Davis,

4
Le migliori pratiche non esistono, se ritieni che le migliori pratiche comprendano che si applicano solo a un determinato contesto. Come sottolineato da altre risposte, ci sono momenti in cui questa regola creerà effettivamente un codice meno gestibile. Man mano che gli strumenti migliorano, questa regola diventa davvero meno rilevante in quanto è molto più facile trovare classi e tipi all'interno di un progetto.
Abombs

263

Lo odio quando la gente pensa in assoluto e dice che non dovresti mai fare questo o quello con qualcosa di soggettivo e schizzinoso come questo, come se tutti dovessimo conformarci a qualcuno stupida idea di giusto e sbagliato. In conclusione: avere più di una classe per file va benissimo se ha senso. Per senso intendo cose come:

  1. Semplifica la digestione e la manutenzione del codice
  2. Rende la soluzione meno fastidiosa (scorrendo innumerevoli file non necessari) e meno lenta
  3. Il team di sviluppo è d'accordo con esso come pratica di codifica locale

Un ottimo esempio del motivo per cui potrei desiderare più classi per file:

Supponiamo di avere alcune dozzine di classi di eccezioni personalizzate, ognuna è una linea 4, potrei avere un file separato per ognuna o potrei raggruppare le eccezioni e avere un file per gruppo. Per me quello che sembra l'approccio più razionale / pragmatico è raggrupparli e avere solo alcuni file, perché è più efficiente tempo / codice saggio (non devo fare clic con il tasto destro del mouse -> Aggiungi classe, rinominare, 50 volte) , mantiene la soluzione meno ingombra e con prestazioni migliori.


92
1000. Comprendere la logica delle migliori pratiche e pensare due volte prima di violarle è grandioso. L'aderenza slava è malvagia.
dsimcha,

19
Qualche decina di classi di eccezione personalizzate ? Non è questa la vera fonte del problema? Non intendo solo essere schizzinoso qui: penso che la maggior parte delle volte le persone vogliano combinare le classi in un singolo file, è perché stanno inutilmente creando troppi tipi. (Detto questo, forse c'è un caso reale in cui alcune decine di classi di eccezione personalizzate hanno senso). Numerose classi di piccole dimensioni, non operative, sono generalmente un segnale di un problema più grande.
Jeff Sternal,

16
Sono d'accordo con te per la maggior parte. Ma le eccezioni sono un caso speciale. Poiché un sistema adeguatamente progettato deve disporre di un'adeguata gestione delle eccezioni per gestire tutti i casi che generano errori legittimi, la maggior parte degli articoli di best practice che ho letto sottolinea la necessità di creare eccezioni specifiche (e alcune volte ne servono molte per coprire tutte le regole aziendali su un progetto di grandi dimensioni) in modo da non riuscire a catturare system.exception che non è affatto una corretta gestione delle eccezioni.
James,

6
Concordo sul fatto che non dovresti seguire saggiamente alcune linee guida senza una buona ragione, tuttavia non sono d'accordo con il fatto che tu debba avere più di una classe in un file. Per me il file dovrebbe essere chiamato dopo la lezione e non contenere più di uno. Alcuni linguaggi (essendo Java uno) prendono atto del fatto che la classe si trova in un file con il nome del file corrispondente al nome della classe. Per me è più facile per i nuovi utenti navigare, quindi per questo motivo credo che la sola classe per file sia la cosa giusta da fare
krystan honor

3
Mantenere una classe per file e sincronizzare il nome del file e il nome della classe è una convenzione, proprio come le variabili di denominazione. Visual Studio è ottimizzato per questo approccio. È più facile per i sistemi di controllo del codice sorgente e per gli sviluppatori aggiunti nel mezzo del progetto. Cercheranno intuitivamente il nome del file corrispondente al nome della classe.
Oybek,

75
static bool GeneralRuleShouldBeFollowed(IGeneralRule rule, IUseCase useCase)
{
    return (rule.Overhead(useCase) 
            < My.PersonalThresholds.ConformismVsPracticality);
}

4
Esiste una regola simile in .NET? Anch'io avrei appena restituito il risultato della dichiarazione. : O
Joan Venge,

50

A volte raggruppo più di una classe all'interno di un file se sono strettamente accoppiati e almeno uno di essi è molto piccolo.

La "best practice" generale consiste nell'avere un file per classe.


7
Se riconosci che sono accoppiati, perché non li stai disaccoppiando?
Il Matt

39
@ The Matt: si chiama evitando l'ingegnerizzazione eccessiva. Se hai alcune classi strettamente accoppiate e il loro disaccoppiamento aggiungerebbe molta complessità rispetto alla quantità di flessibilità pratica che fornisce, allora che senso ha?
dsimcha,

9
A volte ho fondamentalmente delle classi di "dati" che vengono utilizzate solo da questa classe, quindi di solito inserisco la classe di dati nello stesso file dell'utente perché il datac-lass di solito ha poca o nessuna logica in sé e sembra un spreco per creare un nuovo file per esso.
Earlz,

3
@ The Matt: A volte ci sono classi di aiuto che direi accettabili come accoppiamento. Riducono la complessità per parte di un'altra classe ma facilitano ancora uno scopo molto specifico per quella classe.
Jordan Parmer,

6
@TheMatt: Disaccoppia questo: msdn.microsoft.com/en-us/library/… L'accoppiamento tra i moduli è negativo, ma la collaborazione tra le classi all'interno di una funzionalità logica è inevitabile.
Ben Voigt,

25

Oltre agli argomenti ipotetici e concentrandosi invece su Windows .NET con Visual Studio IDE e progetti software in crescita, ha senso in questo contesto avere una classe per file.


In generale, per riferimento visivo nulla batte una classe per file. Veramente.

Non so se Microsoft faccia o non faccia lo stesso, tuttavia hanno creato la partialparola chiave per dividere una classe su più file (questo è ancora più grave). Viene spesso utilizzato per suddividere il codice designer generato automaticamente dal codice personalizzato in stessa classe (ma a volte viene utilizzato per consentire a sviluppatori diversi di lavorare contemporaneamente sulla classe tramite file diversi). Quindi Microsoft vede i vantaggi di più file e tutti hanno in mente più pensieri sull'organizzazione dei file con .NET.

Per le classi nidificate non hai altra scelta che utilizzare un file o almeno le prime parti delle classi in esse contenute. Un file è necessario e va bene in questo caso:

class BicycleWheel {
    class WheelSpoke {
    }
}

Altrimenti perché dovresti tenere più classi in un file? L'argomento "perché sono piccoli" o associati tra loro non contiene molta acqua perché alla fine le tue classi saranno associate ad altre classi.In definitiva, non è possibile dedurre facilmente l'organizzazione in-file degli oggetti in base al loro utilizzo, specialmente quando il software continua a crescere.

Inoltre, se usi le cartelle per gli spazi dei nomi, non avrai mai uno scontro di nomi di file di classe. È anche conveniente individuare una classe per nome file sul file system quando non si trova all'interno di un ambiente di sviluppo come Visual Studio (ad es. Se si desidera modificare rapidamente una classe con Blocco note o qualcosa di rapido / leggero ).

Così tanti buoni motivi ...


Grazie, cosa intendi con "almeno le prime parti di esse"? Vuoi dire che puoi anche separare le classi nidificate?
Joan Venge,

1
Questo è un riferimento indiretto alla partialparola chiave che ho citato.
John K,

1
Per la cronaca, le classi nidificate possono essere partial msdn.microsoft.com/en-us/library/wa80x488(VS.80).aspx Ho cercato questo per curiosità.
John K,

Questo dimostra solo che la vista predefinita dovrebbe essere logica (spazio dei nomi / classe) e non fisica (file).
David Schmitt,

@DavidSchmitt è a questo che serve Class View in Visual Studio.
Zack,

14

Nella stragrande maggioranza dei casi, seguo la regola di una classe per file. L'unica eccezione che faccio regolarmente è la definizione di un enum che è strettamente accoppiato a una classe specifica. In questo caso, includerò spesso la definizione dell'enum nel file di quella classe.


12

Anch'io credo che ci dovrebbe essere un tipo incluso in un singolo file.

C'è un'eccezione a questa regola che deve essere menzionata: avere due classi che differiscono solo per un argomento generico come:

RelayCommand     

e

RelayCommand<T>

Conosci la soluzione alternativa per rendere felice StyleCop in questo caso?
George Polevoy,

Non sono d'accordo, esiste un'altra convenzione per le classi generiche, Foo 1.cs for Foo <T> `e Foo 2.cs for Foo <T, T1>`.
Oybek,

@Oybek: E se avessi Foo <T>, Foo <U> e Foo <U, T>? E adesso?
Andrei Rînea,

@AndreiRinea È impossibile avere Foo<T>e Foo<U>all'interno dello stesso spazio dei nomi. Ma se gli spazi dei nomi differiscono, di solito si trovano in cartelle diverse. Quindi per Foo<T>e Foo<U>dovrebbe essere Foo 1.cs and for Foo <U, T> `Foo`2.cs. Ma comunque una classe per file.
Oybek,

Grazie per l'info! :)
Andrei Rînea,

8

Davvero, questo si riduce alle preferenze personali. Tutti diranno "una classe per file", ma tutti abbiamo i nostri motivi per evitarlo in determinate circostanze. Avevo un grande progetto con circa 300 enumerazioni diverse. Non avrò mai 300 file separati, uno per ogni classe, quando solo alcuni degli enumeratori erano tristati.

Anche per le persone che non riescono a trovare determinate classi se non sono tutte presenti nei file che prendono il nome da quello che sono, c'è un motivo per cui non usi Trova cercando nell'intera soluzione? L'uso di Trova mi fa risparmiare tempo prezioso scorrendo Esplora soluzioni.


Ri: trova - quando cerco la definizione di un tipo, non voglio vedere dove viene usato. Inoltre, find richiede molto più tempo che scorrere un elenco di file in ordine alfabetico di file che probabilmente ho già suddiviso (per spazio dei nomi).
Jeff Sternal,

1
Ho notato che hai indicato che stai lavorando con qualcosa che hai diviso per spazio dei nomi. Purtroppo non siamo tutti abbastanza fortunati da lavorare sempre con progetti che siamo stati in grado di gestire.
Jason M,

6

Non importa quanto sia leggero il contenuto, penso che una classe / interfaccia / ecc. Per file sia essenziale.

Se sto lavorando a una grande soluzione in Visual Studio, voglio essere in grado di vedere i file e non dover scavare dentro per vedere. Anche con strumenti di navigazione come ReSharper, voglio un mapping 1: 1.

Se trovi molti file sorgente con poco o nessun contenuto (magari estendendo una classe ma senza aggiungere nulla ad essa), forse dovresti ripensare il tuo progetto.


5

Trovo che raggruppare una classe con la sua classe di fabbrica standard nello stesso file sia molto utile.


5

Normalmente avrei una classe per file ma normalmente dovresti usare la tua discrezione per vedere se il file potrebbe contenere classi correlate, ad esempio raggruppando le tue eccezioni che possono essere riutilizzate da te e da altri sviluppatori. In questo caso, l'utente deve includere solo un file anziché più file.

Quindi il punto è: la discrezione dovrebbe essere usata !!!


1
Vera saggezza, nessuna regola nella programmazione è dura e veloce.
ChaosPandion,

4

In soluzioni più grandi penso che sia molto utile avere una classe per file e che il file abbia lo stesso nome della classe. Rende molto più facile individuare il codice in cui devi lavorare.


Trovo questo argomento meno prezioso con strumenti come ReSharper. Faccio CTRL + T e inizio a digitare il nome del tipo che sto cercando.
Segna il

3
Sì, ma vuoi che la tua struttura applicativa dipenda da uno strumento di terze parti?
magnus,

1
@magnus Non stavo dicendo che questo non è un motivo per non farlo, ma un argomento meno convincente per qualcuno che lo faccia.
Segna il

4

Lo strumento StyleCop per C # ha regole standard che richiedono non più di una classe di livello superiore in uno spazio dei nomi (più qualsiasi numero di interfacce, delegati ed enumerazioni nello spazio dei nomi).

Nel caso di due o più classi in cui la seconda e le successive sono utilizzate solo dalla prima, quelle potrebbero e dovrebbero essere classi interne, visibili solo alla classe consumatrice.


3
Quando dici "non più di una classe di livello superiore in uno spazio dei nomi", intendi in un namespaceblocco, giusto?
Daniel Pryden,

1
Le regole cui si fa riferimento sono SA1403: il documento AC # può contenere solo un singolo spazio dei nomi. SA1402: Il documento AC # può contenere una sola classe a livello di root, a meno che tutte le classi non siano parziali e dello stesso tipo.
Steve Gilham,

4

Qualche volta una classe per file, ma ...

Quando più classi sono strettamente correlate, più di una classe nello stesso file di origine è, IMHO, MIGLIORE che dedicare un breve file di origine a ciascuna classe. La fonte è più leggibile e compatta (e usando #region la stessa fonte può essere più strutturata di prima).

Considera anche che a volte è NECESSARIO spargere la stessa classe su file diversi (usando il parziale ), dato che avere un file sorgente di oltre 20000 linee non è utile nemmeno con la RAM che ho a disposizione (ma questa è un'altra domanda).


Ovviamente, dovresti cercare di evitare di avere una classe con così tante righe di codice. Direi che anche 1000+ sta diventando troppo grande. Mi rendo anche conto che questo non è sempre possibile nel codice legacy.
Peter,

Se provi a generare codice sorgente (il mio caso è la generazione dell'associazione C # per OpenGL dalle sue specifiche, con migliaia di punti di ingresso) è difficile pensare a una piccola classe per un sacco di dati ...
Luca

3

A volte lascerò una classe piccola con una classe più grande, ma solo se sono strettamente legati come un oggetto e la sua classe di raccolta o fabbrica.

C'è un problema con questo però. Alla fine la piccola classe cresce al punto in cui dovrebbe trovarsi nel suo file, se lo sposti nel nuovo file perdi un facile accesso alla cronologia delle revisioni.

vale a dire.

  • lunedì faccio una modifica alle mie classi x e y nel file y.css
  • martedì ho separato la classe x nel suo file x.css perché è cresciuto fino a diventare grande
  • mercoledì il mio capo vuole vedere cosa ho cambiato in classe x lunedì quindi guarda la cronologia di x.css, solo x.css non mostra la cronologia prima delle modifiche di martedì.

1
E se la "piccola classe" è qualcosa di simile a un derivato di EventArgs, che è destinato a trasmettere informazioni ai destinatari degli eventi della tua classe? Il codice sarebbe reso più pulito o più facile da mantenere spostando una cosa del genere in un altro file? Probabilmente, una cosa del genere dovrebbe essere una classe pubblica nidificata, ma anche quelle sembrano essere disapprovate. Per quanto riguarda la cronologia delle revisioni, la classe non sarebbe apparsa dal nulla nel log delle modifiche e non dovrebbe commentare la nota in codice da dove proviene?
supercat

Lo classificherei come very tightly relatedclasse e lo lascerei a condizione che occupi solo una piccola quantità di spazio sullo schermo e non venga utilizzato per lo stesso scopo da qualsiasi altra classe.
Gingerbreadboy,

3

È davvero un problema? :)
Classi davvero piccole, proprio come le enumerazioni, possono essere unite ad altre. C'è una regola da seguire: mettere insieme solo le classi che hanno qualcosa in comune.

Come digressione: in uno dei miei progetti ho un file che contiene 150 classi. Il file ha 10000 righe di codice. Ma è generato automaticamente, quindi è pienamente accettabile :)


1
Ho uno stesso file con 120 classi e 750 sottoclassi con mezzo milione di righe, è anche generato automaticamente. Il problema è: se faccio clic su di esso, devo riavviare perché tutto si ferma.
Behrooz,

3

Uno dei motivi per mettere più classi correlate in un file è che il povero bastardo che usa la tua API non deve passare mezza giornata a scrivere la dichiarazione di importazione bollettino e il povero bastardo che deve mantenere il codice non deve spendere metà un giorno scorrendo il bollettino della dichiarazione di importazione. La mia regola empirica è che più classi appartengano allo stesso file se si usasse quasi sempre un grande sottoinsieme di esse contemporaneamente anziché solo una alla volta.


1
Cosa intendi per "bollettino della dichiarazione di importazione"? "Utilizzo delle dichiarazioni"? Ma le classi non saranno nello stesso spazio dei nomi?
Joan Venge,

3
@Joan: intendo dover importare 15 moduli diversi ma correlati per realizzare qualcosa di veramente semplice. Forse non si applica specificamente a C #. Non conosco davvero C #, ma in altre lingue è un problema piuttosto fastidioso.
dsimcha,

Grazie sì, ecco perché ero confuso.
Joan Venge,

2

Lo faccio, ma solo quando le classi sono correlate in modo figlio-genitore e le classi figlio vengono utilizzate SOLO dal genitore.


Grazie, ma perché non lo separi in un altro file? Solo curioso.
Joan Venge,

@henchman l'ha detto molto più eloquentemente di me, specialmente quando l'oggetto figlio è molto piccolo. Solitamente, la classe figlio è proprietà solo con forse solo un po 'di logica
CResulti

2

Di solito resto con una classe per file. Ma farò eccezioni per gruppi di costrutti simili utilizzati a livello di progetto. Per esempio:

  • Un EventArgs.cs che contiene eventuali EventArgssottoclassi, poiché di solito sono solo 5-10 righe di codice ciascuna, ma in genere vengono utilizzate da diverse classi diverse. In alternativa, potrei mettere le EventArgsclassi nello stesso file della classe che dichiara gli eventi.
  • Un Delegates.cs che contiene i delegati utilizzati in tutto il progetto, poiché di solito sono solo 1 riga ciascuno. Ancora una volta, l'alternativa è metterli nello stesso file con la classe che li espone / li consuma.
  • Un Enums.cs che contiene enums usati in tutto il progetto. (Se ce n'è uno enumusato solo da una classe, di solito ce la farò private.)

2

Un altro voto per una classe per file con il nome del file uguale alla classe. Per me, aiuta con la manutenibilità a lungo termine. Posso facilmente consultare il repository e vedere quali classi fanno parte di una soluzione senza dover aprire il progetto o alcun file.


2

Lo seguo il 99% delle volte. È buono seguire gli standard, ma credo anche che la flessibilità abbia il suo posto. A volte sembra solo una sciocca perdita di tempo per spezzare le cose. In quei tempi, mi dimentico e scrivo solo il mio codice.


2

Le risposte finora sembrano ruotare attorno alle eccezioni delle persone alla regola, quindi ecco la mia: mantengo le classi e le classi "buddy" dei loro metadati quando uso il pacchetto DataAnnotations in .NET3.5 SP1. Altrimenti, sono sempre in file separati. Sai, la maggior parte delle volte. Tranne quando non lo sono.


2

Lo faccio solo raramente. Ad esempio, se esiste un elenco o una struttura strettamente correlati alla classe, ma troppo banali per essere separati da soli.

O una classe separata per contenere alcuni metodi di estensione per quella classe principale.


1

One case could be:quando le tue classi formano congiuntamente una module / unitche serve alcune classi principali come helper classes, altre sagge no .

dai un'occhiata al codice sorgente del progetto ASP.NET MVC 2.0 . Segue rigorosamente questa regola


1

Mi piace l'idea di creare classi più piccole e assicurarmi che la classe stia facendo solo ciò che dovrebbe fare. Se hai più classi che stanno contribuendo a risolvere un singolo problema, non c'è nulla di male nel metterle insieme nello stesso file.

Non seguirei le pratiche della SM in quanto non sono le MIGLIORI PRATICHE!


1

Un altro punto che non ho visto menzionare da nessuno è che quando sviluppi usando la regola di una classe per file puoi facilmente vedere cosa sta usando quella specifica classe.

Ad esempio: potresti avere due classi in cui una classe usa Linq e l'altra no.

Se queste classi fossero nello stesso file non saresti in grado di dire senza guardare attraverso il codice quale classe usa cosa. Quando è una classe per file, tutto ciò che devi fare è guardare la parte superiore del file per vedere esattamente cosa viene usato in quella classe. Aiuta se stai mai migrando verso una nuova libreria, ecc.


0

Un altro motivo per una classe per file che non è stato menzionato finora nelle risposte postate è che una classe per file semplifica la comprensione dell'impatto di un PR durante una revisione del codice. Riduce anche i conflitti di unione.

Quando qualcuno pubblica un PR per un feedback posso guardare l'elenco dei file modificati e vedere immediatamente qualsiasi sovrapposizione con ciò su cui potrei lavorare. A seconda della sovrapposizione, potrei voler esaminare il loro codice più profondamente o dargli un OK perché sono abbastanza sicuro che non influenzerà le mie modifiche.

Quando due persone lavorano in un file multi-classe ed entrambi aggiungono una dipendenza, è probabile che si verifichi un conflitto di unione nel using blocco in alto. La separazione delle classi in file separa le dipendenze in modo da poter vedere cosa sta usando ogni classe e non si verificano conflitti come questo.

Ci sono eccezioni a questa regola (interfaccia + implementazione, enum, ...) ma è un punto di partenza migliore rispetto al contrario che in genere consente agli sviluppatori junior di raggruppare tutti i tipi di classi non correlate nello stesso file.

Una classe per file è una chiara regola non ambigua non soggetta a interpretazione.


Le classi correlate in un file sono soggette a preferenze e interpretazioni personali (come puoi vedere da tutte le altre risposte qui su quando è OK usare) e quindi è una regola scadente.


-1

Il avanti e indietro è stato molto interessante e apparentemente inconcludente, anche se la mia impressione generale è che una mappatura 1-1 tra classi e file sia l'opinione della maggioranza, anche se con alcune eccezioni da persona a persona.

Sono curioso di sapere se una qualsiasi delle tue risposte varia a seconda che tu stia: (1) sviluppando un'app di Windows Form, un'app Web, una libreria o altro; o (2) utilizzando Visual Studio o no. Usando VS, sembrerebbe che la regola di una classe per file implichi anche una classe per progetto VS poiché il consenso in altri thread sembra essere che le soluzioni / i progetti VS debbano essere rispecchiati nella struttura e nella denominazione dei file / directory. In effetti, la mia impressione è che il consenso sia quello di avere il nome del progetto = nome dell'assemblaggio = nome dello spazio dei nomi (nidificato), il quale verrebbe quindi rispecchiato nella struttura e nella denominazione dei file / directory. Se queste sono le giuste linee guida (o regole), allora tutti questi meccanismi organizzativi apparentemente ortogonali sarebbero comunque mantenuti sincronizzati.


-1

Sì, per motivi di leggibilità dovremmo avere un file per classe !. Sono appena entrato in un progetto. Vedo molte classi in un unico file. rende così difficile per un nuovo ragazzo capirlo. non dovremmo pensare alla manutenibilità? quando sviluppiamo un software? molte volte lo sviluppo continuerà da altri sviluppatori. Abbiamo spazi dei nomi per organizzare le nostre cose, non abbiamo bisogno di file per farlo !.


-5

Un elemento di codice per file, sì.

Tutto il resto è una negligenza - e, francamente, un segno di vittima RAD.

Non appena si avvia il corretto sviluppo del software (IoC, schemi di progettazione, DDD, TDD, ecc ...) e si lascia il parco giochi "omg consente di farlo, non ho idea di come, ma vengo pagato", si vedrà che questa regola conta davvero, davvero.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.