Non usare "statico" in C #?


109

Ho presentato una domanda che ho scritto ad alcuni altri architetti per la revisione del codice. Uno di loro quasi immediatamente mi ha risposto e ha detto "Non usare" statico ". Non puoi scrivere test automatici con classi e metodi statici." Statico "è da evitare".

Ho controllato e 1/4 delle mie classi sono contrassegnate come "statiche". Uso statico quando non ho intenzione di creare un'istanza di una classe perché la classe è una singola classe globale utilizzata in tutto il codice.

Ha continuato a menzionare qualcosa che coinvolge derisione, tecniche IOC / DI che non possono essere utilizzate con codice statico. Dice che è un peccato che le librerie di terze parti siano statiche a causa della loro non testabilità.

Questo altro architetto è corretto?

aggiornamento: ecco un esempio:

APIManager: questa classe mantiene i dizionari delle API di terze parti che sto chiamando insieme al prossimo tempo consentito. Impone limiti di utilizzo dell'API che molte terze parti hanno nei loro termini di servizio. Lo uso ovunque sto chiamando un servizio di terze parti chiamando Thread.Sleep (APIManager.GetWait ("ProviderXYZ")); prima di effettuare la chiamata. Tutto qui è thread-safe e funziona benissimo con il TPL in C #.


37
staticè ok; static i campi devono essere trattati con molta attenzione
Marc Gravell

2
Perché dovrebbe scrivere test per librerie di terze parti? Di solito non testate il vostro codice assumendo che i creatori della libreria di terze parti abbiano fatto il loro test?
No,

3
Quella particolare obiezione è un po 'troppo generica per me. Nella maggior parte dei casi non vorrai mocj la classe statica, derideresti gli argomenti. Campo statico in cui era una classe aggregata che aveva bisogno di essere derisa, quindi sì.

8
Chiaramente i tuoi colleghi hanno una grave condizione: un OOPus eritematoso sistemico acuto. Hanno bisogno di un trattamento al più presto!
SK-logic,

6
C'è una sezione di risposte per fornire risposte, non capisco perché le persone commentino nel tentativo di dare a qualcuno una risposta / suggerimento ... Sconfigge lo scopo di StackExchange, sicuramente un commento sarebbe di richiedere maggiori informazioni.
peteski,

Risposte:


121

Dipende dal fatto che le classi statiche mantengano lo stato o meno. Personalmente, non ho alcun problema con le funzioni stateless raggruppate in una classe statica.


111
Corretta. Le funzioni statiche che non hanno effetti collaterali sono le funzioni più testabili di tutte.
Robert Harvey,

9
+1 a quello, ma con una clausola di sicurezza: quasi ogni algoritmo astratto è senza stato, ma ciò non significa che dovresti implementarlo come classe o metodo statico senza stato. L'implementazione in questo modo fa sì che tutto il codice che lo utilizza non sia strettamente collegato ad esso. Ciò significa ad esempio che se si implementa l'algoritmo di ordinamento come metodo statico e lo si utilizza attraverso il progetto, non è possibile sostituire temporaneamente l'ordinamento a un altro algoritmo, ad esempio a scopo di test e per restringere l'area del problema. Questo è un enorme ostacolo in molti casi. A parte questo, statico è totalmente OK se usato in modo ragionevole.

11
In breve: sono le funzioni più testabili, ma non necessariamente rendono l' altro codice più testabile! Questo è ciò che probabilmente intendeva l'architetto.

4
@ tereško: il linguaggio C # richiede che i metodi statici facciano parte di una classe statica, se non si desidera creare un'istanza della classe per chiamare il metodo. Forse intendi "istanza" e non "classe".
Robert Harvey,

8
@quetzalcoatl: è perfettamente legale passare un delegato (cioè un algoritmo diverso) a un metodo statico; i metodi di estensione in Linq sono tutti metodi statici. Esempio: msdn.microsoft.com/en-us/library/…
Robert Harvey

93

È troppo generale al riguardo. Ha ragione, ostacola i test. Tuttavia, staticclassi e metodi hanno il loro posto e dipende molto dal contesto. Senza esempi di codice non puoi davvero dirlo.

Uso statico quando non ho intenzione di creare un'istanza di una classe perché la classe è una singola classe globale utilizzata in tutto il codice.

Questo può essere un grave odore di codice. Per cosa stai usando le lezioni? Conservare i dati? Per accedere a un database? Quindi ha ragione. In questo caso dovresti esaminare l'iniezione di dipendenza, poiché una tale classe statica è effettivamente un singleton implicito.

Se li stai usando per metodi di estensione o helper che non cambiano lo stato e operano solo sui parametri che fornisci, quelli di solito vanno bene.


3
+1 per menzionare che la dichiarazione era generale e menzionare i metodi di estensione, che possono anche essere testati e le dipendenze possono ancora essere derise per quanto ne so.
No,

4
Aye statico come istanza di singola classe come singleton implicito, che diventerà disordinato ....

"Per accedere a un database?" perchè no ? se l'adattatore di tabella non è statico, quindi creare una classe statica @ set di dati fortemente tipizzato, nulla di sbagliato in esso .... a meno che non si dimostri il punto con riferimento o esempio?
Matematica

27

Ho controllato e 1/4 delle mie classi sono contrassegnate come "statiche". Uso statico quando non ho intenzione di creare un'istanza di una classe perché la classe è una singola classe globale utilizzata in tutto il codice.

La cosa migliore da fare è provare e testare l'unità il tuo codice. Prova a progettare test ripetibili, indipendenti, semplici e prova solo un metodo alla volta. Prova a eseguire i test in un ordine casuale diverso. Riesci a ottenere una build "verde" stabile?

Se sì, questo è un punto valido per difendere il tuo codice. Se, tuttavia, hai difficoltà, forse dovresti scegliere metodi basati su istanze.


7
Questo è il punto che mi ha colpito anche. Alcune classi di stile "helper" statiche e apolidi vanno bene, ma se hai il 25% completo di tutte le tue classi come statiche, è improbabile che i tuoi sforzi siano limitati a quel caso.
Matthew Scharley,

2
@MatthewScharley - probabilmente ha preso 4 lezioni, e 1 di esse è statico :)
Alexus,

1
Fai attenzione se usi questi stibc (come i singoli), che in seguito creare più istanze potrebbe essere un lavoro difficile. Come ad esempio la creazione di un'applicazione che gestisce un documento, si verificano problemi in seguito quando si desidera gestire più documenti.
Michel Keijzers,

11

Le risposte già pubblicate coprono molti punti davvero positivi, ma ce n'è uno che sembra mancare:

I campi statici non vengono mai raccolti.

Questo è molto importante se si dispone di un'app con molti vincoli di memoria e questo modello può essere molto comune quando le persone tentano di implementare una cache.

Le funzioni statiche non sono altrettanto brutte, ma tutti gli altri hanno già affrontato questo argomento in modo sufficientemente dettagliato.


10

Uno dei vantaggi che si ottengono da IoC / DI è che la maggior parte delle interazioni tra le classi sono negoziate tra le interfacce. Ciò semplifica il test delle unità perché le interfacce possono essere derise automaticamente o semi-automaticamente, e quindi ciascuna delle parti può essere testata per input e output. Inoltre, al di là della testabilità, mettere interfacce tra tutto ciò ti consente di avere il codice più modulare possibile: puoi facilmente sostituire un'implementazione senza troppe preoccupazioni che stai rovinando le dipendenze.

Poiché C # non ha metaclassi, una classe non può implementare un'interfaccia usando funzioni statiche, quindi le funzioni statiche delle classi finiscono per rovinare qualsiasi sforzo per implementare un modello a oggetti IoC / DI puro. Vale a dire, non è possibile creare una simulazione per testarli e è necessario creare dipendenze reali.

Se la tua azienda / progetto è investito molto nel fare l'IoC, questo ovviamente è una preoccupazione ragionevole e il dolore che stai attraversando è per il guadagno di tutti. Dal punto di vista architettonico, tuttavia, personalmente non penso che nessun modello debba essere seguito alla tomba. Ci sono alcune cose che hanno senso implementare usando metodi statici - la strategia di progettazione singleton, per esempio. Sono meno propenso alle classi statiche perché penso che tendano a perdere i vantaggi di OO, ma ci sono momenti in cui una classe di libreria è probabilmente un'espressione più naturale di qualcosa di un motore.


Commenter mi ricorda i metodi di estensione, che ovviamente devono essere in classi statiche in C #. Questo è legittimo ed è un esempio di come l'IoC puro non funzioni molto bene in C #, almeno se stai cercando di sfruttare l'ampiezza della lingua.


1
Se una classe viene resa statica per i giusti motivi e correttamente implementata, come ad esempio i metodi di estensione, è ancora testabile in quanto non avrebbe dipendenze esterne e sarebbe autonoma e come tale non dovrebbe richiedere beffe. La necessità di un'interfaccia non deve essere guidata dal requisito di progettazione per soddisfare al meglio un requisito aziendale non per il mantenimento di un progetto IoC / DI puro?
No,

1
Siamo d'accordo sul punto generale che dovresti scegliere lo strumento giusto per il lavoro giusto e non essere ammanettato dalla filosofia. Se, tuttavia, esiste una cultura ben consolidata nel tuo progetto di puro IoC / DI, danneggerà tanto quanto convertire una soluzione tradizionalmente top-down in IoC / DI. L'ingegneria deve considerare i costi di transizione. A proposito, non credo che i metodi di estensione ti portino lì. Penso che una soluzione migliore per il comportamento di IoCish con le classi statiche sarebbe quella di scegliere più classi tra al momento della compilazione con le direttive del preprocessore, stile C
jwrush

1
Er. Sono stupido Intendevi una classe statica per i metodi di estensione HOLDING, che, ovviamente, è come devi farli. Sì, certo, vuoi una classe statica per quello scopo. Mi è passato di mente: e questo dimostra che C # non era destinato a IoC. :)
jwrush

Sì, hai ragione, se esiste una cultura esistente in un progetto esistente sarebbe più dannoso che aiutare a cambiare il modo in cui le cose vengono fatte.
No,

5

Le classi statiche sono talvolta utilizzate in modo improprio e dovrebbero essere:

  • un singleton (dove la classe non statica ha un membro statico e una variabile di istanza statica pubblica per recuperarla / crearla una sola volta.
  • un metodo in un'altra classe (dove lo stato conta). Se hai un membro che non utilizza dati da quella classe, probabilmente non dovrebbe far parte di quella classe.

Potrebbe anche essere una cosiddetta funzione "utility" e parte di una classe di utility. Le classi di utilità sono classi che contengono solo metodi statici e fungono da funzioni di supporto senza alcun contesto.


@topomorto Perché la classe non dovrebbe essere istanziata dal suo costruttore risultante in più oggetti / istanze, ma da un metodo speciale (normalmente istanza ()) <che restituisce sempre la stessa istanza, istanziata dopo la prima chiamata a istanza ().
Michel Keijzers,

@topomorto Ho appena controllato e hai perfettamente ragione, solo alcuni membri sono statici (la variabile di istanza e la funzione di istanza). Cambierò di conseguenza la mia risposta; grazie per la menzione.
Michel Keijzers,

4

I metodi statici vanno bene e hanno un posto legittimo nella programmazione. Sembra che il tuo architetto abbia un framework di test che non supporta completamente i metodi statici. Se il tuo codice fa parte di un progetto più grande, è importante rispettare le linee guida degli architetti. Tuttavia, non lasciare che questo progetto ti scoraggi dall'uso di metodi statici quando è appropriato farlo.


1

Ho usato proprietà statiche per cose comuni a tutte le istanze di una classe. E ho usato metodi statici per ottenere gruppi di oggetti di classe. Non sono affatto un esperto, ma finora ha funzionato per me.

Esempio di PHP:

class vendorData {
  private static $data_table = 'vendor_data'; // static property
  private $id; // instance property
  private $name; // instance property

  public function __construct($vendor_id) {
    if(!self::validId($vendor_id) ) { // static method
      return false; // id doesn't exist
    }
    $this->id = $vendor_id; // id has been validated
    $this->getProperties(); // object method
  }

  private static function validId($vendor_id) {
    // send db query to find if the id exists
    // return true/false;
  }

  private function getProperties() { // object method
    $sql = "SELECT * FROM `{self::$data_table}` // using static property
        WHERE `id` = {$this->id}"; // using object instance property
    // get resultset
    foreach($result as $property => $value) {
      $this->$property = $value; // object instance properties all set
    }
  }

  // and here
  public static function getBy($property,$value) { // static method to return object array
    $sql = "SELECT `id` FROM `{self::$data_table}` // using static property
      WHERE `$property` = '$value'";
    // send query, get $ids array
    $obj_array = array();
    foreach($ids as $id) {
      // create array of current class objects using special static keyword
      // meaning of static here is different than in property/method declarations
      $obj_array[$id] = new static($id);
    }
    return $obj_array;
  }
}

1

Dirò che i principi che hanno sono ok ma l'affermazione (non usare la statica) potrebbe essere sbagliata. Quanto costa la copertura del tuo codice? se il numero è alto e ti senti a tuo agio con il test unitario della tua app, allora stai bene. In caso contrario, suggerirei di rivedere il codice. Potresti scoprire che le classi statiche sono la ragione o no, dipenderà da molte cose.


-3

Le classi con metodi statici abusano dei principi di OOP. Non è già un OOP, è una programmazione orientata alla classe COP.

Raramente hanno una "personalità" chiara (qui uso la mia metafora umana preferita ) o identità. Quindi non sanno chi sono. Questo punto da solo è sufficiente per sbarazzarsi di questo concetto in OOP, ma ce ne sono altri.

Il prossimo è una conseguenza del primo punto. Dal momento che tali classi non sanno chi sono, tendono ad essere grandi o grandi.

Il terzo rende il codice assolutamente non gestibile. L'uso di metodi statici introduce dipendenze nascoste . Appaiono dappertutto e non sai mai cosa sta succedendo nella tua classe. Non puoi esserne sicuro guardando la firma del costruttore.

Lentamente, ma inevitabilmente, il tuo codice sta diventando sempre meno coeso, poiché le dipendenze nascoste sono scarsamente controllate. Sembrano invisibili. Ed è così facile aggiungerne un altro! Non è necessario modificare la firma di alcun metodo. Tutto quello che devi fare è chiamare un metodo statico. E che brutto codice segue ...

Ok, probabilmente hai già indovinato. L'accoppiamento stretto è spesso associato a bassa coesione. Se ci sono molti client che usano una classe, allora l'accoppiamento sta diventando più stretto. E c'è una grande seduzione nell'usare una classe o un metodo già scritti che richiedono solo una piccola correzione. Un solo parametro parametro parametro.

Cosa usare invece di metodi statici? Bene, il mio suggerimento è OOP. Perché non provarlo?

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.