Perché usare le classi parziali?


72

A mio avviso, la partialparola chiave non fa altro che consentire a una classe di essere suddivisa tra più file di origine. C'è qualche motivo per farlo se non per l'organizzazione del codice? L'ho visto usato per quello nelle classi di UI generate.

Sembra una ragione scadente per creare un'intera parola chiave. Se una classe è abbastanza grande da richiedere più file, probabilmente sta facendo troppo. Ho pensato che forse potresti usarlo per definire parzialmente una classe per un altro programmatore da completare da qualche parte, ma sarebbe meglio creare una classe astratta.


10
Non è nemmeno una "parola chiave intera". È una parola chiave contestuale . partialsignifica qualcosa solo quando viene prima class. Puoi usarlo come nome identificativo in altre parti del codice, ecc.
Dean Harding,

4
Se li usi, e non è per il codice generato, ti odio. Non tu personalmente, ma in generale. Odio cercare il codice attraverso classi parziali.
Steven Evers,

3
Non è difficile "dare la caccia al codice", tutti i metodi di classe vengono comunque visualizzati nel menu a discesa in VS, indipendentemente dalla parte del parziale che stai osservando. Anche la navigazione di altri codici funziona bene (o navigazione in stile R # "intelligente" o buon vecchio shift-ctrl-f
Steve

2
È un fraintendimento che le classi parziali debbano essere in file separati.
Bart,

Risposte:


110

È molto utile in ogni scenario in cui una parte della classe viene generata da alcuni strumenti personalizzati perché consente di aggiungere una logica personalizzata al codice generato senza ereditare la classe generata. Btw. ci sono anche metodi parziali per lo stesso motivo.

Non si tratta solo dell'interfaccia utente, ma anche altre tecnologie come Linq-To-Sql o Entity Framework lo usano abbastanza pesantemente.


44
Inoltre, ti consente di mantenere il codice sotto il controllo della versione lasciando il codice generato fuori dal controllo della versione.
Frank Shearar,

3
Buon punto, non ci ho mai pensato. L'ho visto solo abusato in pratica però. Un modo per i programmatori (schifosi) di mantenere gestibili le dimensioni del file.
Martin Wickman,

1
I migliori esempi che ho usato sono: a) per contesti di dati Linq to SQL in cui si desidera estendere le classi; b) estendere le classi passate dai servizi web. In nessun caso si tratta di un abuso né di un modo per mantenere gestibili le dimensioni dei file ... Sarei radicalmente infelice nel vederlo usato in quel modo (e farei dei passi ...)
Murph

3
Le classi WinForm lo usano per nascondere tutti i controlli di creazione del codice del designer (particolarmente importante perché al designer piace riordinare in modo casuale tutto il codice che crea causando enormi problemi durante la diff / blaming) e se stai lavorando con XML, lo strumento XSD può creare di nuovo classi parziali dal file di schema consentendo di separare il codice dall'ordinamento generato automaticamente.
Dan Neely,

2
@MartinWickman non è necessariamente scadente. È un buon punto di partenza per il refactoring degli oggetti God nel codice legacy. Prova a chiarire il panorama dividendo prima le grandi classi in file separati. (So ​​che il tuo commento è molto vecchio)
Konrad Morawski,

26

Come dici tu, è spesso usato per separare il codice generato. Spesso non ha nulla a che fare con la dimensione di classi / file.

Il vantaggio di separare il codice generato è di stile. Il codice generato può essere piuttosto brutto e illeggibile e fallirebbe molti standard di codifica (e controlli StyleCop), ma va bene, nessuno deve leggerlo o mantenerlo direttamente. Quindi, se lo "nascondi" in un altro file, puoi concentrarti sul fatto che il resto della classe sia conforme allo standard, passa i controlli StyleCop e così via.

Un'altra area in cui l'ho usata è quella in cui una classe implementa più interfacce, può essere abbastanza bello separare l'implementazione in file separati, anche se si tratta più di una questione di preferenze personali, non ho mai visto standard di codifica richiesti ( o prevenire) questo.


9
Non avevo mai pensato di usare i parziali di classe per separare le implementazioni dell'interfaccia, questa è un'idea eccellente.
Mark Booth,

Riguarda molto più dello stile. Gli sviluppatori di un generatore di codice dovrebbero saltare attraverso i cerchi per consentire di modificare il codice in un file gestito dal generatore, e anche in questo caso sarebbe problematico. Il vantaggio n. 1, per me, ti sta dando un posto dove scrivere codice che il generatore non potrà / non potrà toccare. Questo è un vantaggio piuttosto tangibile.
Daniel,

19

Uno degli sviluppatori in cui lavoro mi è venuto in mente un buon uso per loro un paio di settimane fa nel refactoring di enormi classi God che sono sfuggite al controllo e hanno molti metodi pubblici: separando le funzioni logiche di ogni bit di la classe in classi parziali separate puoi separare fisicamente la classe nelle unità più atomiche che dovrebbero essere le classi senza interrompere alcuna funzionalità esistente, permettendoti di vedere cosa è comune e cosa non lo è. Con questo come primo stadio puoi quindi dividere più facilmente i parziali nelle loro classi indipendenti e implementarli in tutta la base di codice. Ho pensato che fosse una bella idea.

Tuttavia, in generale, penso che dovrebbero essere usati solo per aumentare le classi generate dalla macchina quando si scrive un nuovo codice.


1
Vedo come potrebbe essere utile. Se questa è la giustificazione per essere nella lingua o no ... Non lo so. Tuttavia, il codice generato dalla macchina è un'altra storia.
Michael K,

2
Sì, non è una giustificazione per essere nella lingua, ma è un modo interessante per usarli.

18

Posso pensare ad alcuni scenari utili in cui i parziali hanno un senso, la maggior parte di essi mi sto usando nei miei progetti:

  • Per separare il codice generato dallo strumento / IDE / Designer dal codice che si sta mantenendo. Un buon esempio è il Form.Designer.csfile che contiene il codice generato dal designer per le applicazioni Windows Form. Molti altri formati .NET hanno anche alcuni codici generati da strumenti, che possono essere potenzialmente rigenerati quando si crea il progetto e quindi tutte le modifiche personalizzate verranno eliminate. La separazione ti aiuterà a proteggere il codice e le modifiche da tali modifiche automatizzate.

  • Quando si implementano più interfacce con molto codice nell'implementazione. Io tendo ad usare un file parziale separato per ogni tipo di interfaccia, la denominazione in questo modo: {Class}.{Interface}.cs. È facile per me vedere nell'IDE quale interfaccia si {Class}sta implementando e come.

  • Quando la classe deve contenere una o più classi nidificate , in particolare con codice sufficiente per essere inserita in un file separato. Vorrei attenermi al modello sopra e utilizzare la {Class}.{NestedClass}.csconvenzione di denominazione per ogni classe nidificata. Una pratica simile è già menzionata dalla risposta di Virtlink .

  • Quando scrivo staticclassi che conterranno metodi di estensione . Sarà spesso il caso di fornire la stessa logica del metodo di estensione a classi o interfacce simili, ad esempio Reversesu raccolte generiche e non generiche. Vorrei mettere tutti i metodi di estensione per una singola classe o interfaccia in un parziale separato della classe statica. Ad esempio, avrei tutti i metodi di estensione per l' IListinterfaccia in un posto e gli stessi metodi che sono IList<T>in un altro file. Un altro approccio sarebbe quello di mettere lo stesso metodo (tutti i suoi sovraccarichi) nello stesso parziale, con tutte le possibili classi per il thisparametro - come avere tutti iReverseimplementazioni in un file. Dipende da quale si giustificherebbe meglio la separazione in termini di volume di codice o da alcune convenzioni interne a cui voi o la vostra organizzazione potreste attenervi.

  • Non lo uso, ma ho visto alcune persone C / C ++ apprezzare l'approccio che descriverò qui: creare un parziale per la classe solo con metodi parziali . Questo assomiglia al modo C / C ++ per definire le interfacce e separare la dichiarazione del metodo dall'implementazione.

  • Separazione da preoccupazioni puramente logiche . Se ti capita di lavorare con una classe di grandi dimensioni che combina più di un set logico di operazioni in sé, puoi separare ciascun codice logicamente correlato in un parziale separato. Di solito l'esistenza di tali classi è contro il principio di separazione delle preoccupazioni , ma spesso viene osservato il caso della vita reale, specialmente per basi di codice più vecchie. Il collega utente Steve Evers li ha menzionati nella sua risposta a questa domanda , riferendosi ad essi con il nome di god object. Personalmente userei l'approccio delle classi parziali per dividere il codice prima che avvenga qualsiasi refactoring di file così grandi, per facilitare il mio lavoro e rendere il refactoring più trasparente. Ciò ridurrà anche i conflitti che potenzialmente causerò quando sono coinvolti sistemi di versioning come SVN.


14

Non l'ho visto menzionato da nessuno: uso partialle classi nidificate nei propri file.

Tutti i miei file di codice contengono solo una classe, struct, interfaccia o enum. Rende molto più facile trovare la definizione di un oggetto quando i nomi dei file mostrano il nome della cosa che stai cercando. E poiché Visual Studio tenta di far corrispondere le cartelle del progetto agli spazi dei nomi, i nomi dei file devono corrispondere alle classi.

Questo significa anche che una classe NestedClassnidificato all'interno MyClassavrà un suo file nei miei progetti: MyClass.NestedClass.cs.

partial class MyClass
{
    private class NestedClass
    {
        // ...
    }
}

1
Mi chiedo perché si dovrebbe votare in negativo questa risposta. La pratica menzionata non è né un modello anti-modello, né causerebbe problemi che conosco.
Ivaylo Slavov

1
Questo risolve anche il problema di mantenere piccoli i file di classe (in termini di righe di codice) pur consentendo l'incapsulamento corretto del codice che appartiene a una classe interna.
redcalx,

10

Con l'eccezione dell'utilizzo del codice generato, li ho visti sempre e solo usati nel tentativo di coprire gli oggetti divini . Cercare di capire una nuova base di codice o navigare attraverso più file sorgente, che sono lo stesso oggetto, è davvero fastidioso.

Quindi quando chiedi Why use partial classes?rispondo: a meno che tu non stia usando il codice generato, non farlo.


+1. È la prima volta che vedo questi tipi di oggetti che vengono chiamati un nome (oggetti divini) ed è persino ufficiale. Non è mai troppo tardi per imparare qualcosa di nuovo.
Ivaylo Slavov

6

L'organizzazione del codice è l'unica ragione, ma va più in profondità di quanto sembri a prima vista. Se si dispone di classi parziali in cui vengono generate parti, è possibile:

  • Rigenera il codice senza dover rilevare le modifiche manuali nelle classi in cui scrivi (in modo da non sovrascrivere quelle parti).
  • Escludere le classi parziali generate dalla copertura del test, dal controllo del codice sorgente, dalle metriche, ecc.
  • Usa il codice generato senza forzarti a basare la tua strategia di ereditarietà su di esso (puoi comunque ereditare da altre classi).

1

Ci sono anche alcuni posti in cui le classi generate / implicite sono dichiarate parziali, quindi se il Dev ha bisogno di estenderle, hanno pieno accesso senza doversi preoccupare di ereditare e scavalcare dappertutto. Guarda le classi generate nell'area temp di asp.net dopo aver eseguito per la prima volta un sito webform sotto IIS se vuoi provare a prendere alcuni esempi.


1

Mi viene in mente un uso sporco per loro.

Supponiamo di avere alcune classi che necessitano di funzionalità comuni, ma non si desidera iniettare tale funzionalità nella catena di ereditarietà sopra di esse. Oppure, hai un set di classi che usano una classe helper comune.

Fondamentalmente vuoi fare l'ereditarietà multipla ma C # no likey. Quindi quello che fai è usare parziale per creare ciò che è essenzialmente un #include in C / C ++;

Inserisci tutte le funzionalità in una classe o usa la classe helper. Quindi copia l'helper X volte e rinominalo in classe parziale A, B, C. e metti parziale sulle tue classi A, B, C.

Disclaimer: Sì, questo è male. Non farlo mai, specialmente se farà arrabbiare gli altri con te.


Mixins. Questo potrebbe essere combinato con T4 per evitare la copia manuale ...
Peter Taylor,

Come notato, questo ricorda da vicino i mixin. Tuttavia, ti stai avvicinando a un concetto noto come "tratti" e gestiranno in modo sicuro ciò che stai cercando di ottenere. Ho una descrizione indipendente dal linguaggio su publius-ovidius.livejournal.com/314737.html Ho cercato un'implementazione pulita per C #, ma non ne ho visto uno.
Ovidio,
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.