C # - Perché i prefissi sui campi sono scoraggiati?


19

Ai vecchi tempi, abbiamo fatto la notazione ungherese. Questo è ora considerato passé e per la maggior parte non lo uso più, ma trovo ancora l'uso del m_prefisso per indicare i campi dei membri.

Per me, se sto leggendo il codice di qualcun altro, e vedo questo:

count = 3;

Presumo che countsia una variabile locale per quella funzione e sto facendo qualcosa che verrà usato altrove nella funzione; se vedo questo:

m_count = 3;

Mi rendo subito conto che sto aggiornando lo stato dell'oggetto.

Le linee guida di stile Microsoft dicono che è il modo sbagliato di fare le cose. Dovrei nominare i miei campi non pubblici esattamente come variabili temporanee definite all'interno della funzione. No m_, nemmeno un semplice trattino basso.

Sì, possiamo definire il nostro stile di codifica, ma poi devo lottare con vari strumenti di analisi del codice statico, per convincerli che il nostro modo di fare le cose è OK.

Sono felice di cambiare allo stile di Microsoft, ma mi piacerebbe sapere perché le cose sono come sono.

Perché ora è considerato male poter dire se una variabile è funzione locale o un membro?

PS Questo è molto simile a Quali sono le migliori pratiche attualmente considerate per quanto riguarda la parola chiave "this" davanti al campo e i metodi in c #? , ma sto chiedendo di m_nothis.

PPS Vedi anche Perché Microsoft ha fatto parametri, variabili locali e campi privati ​​hanno la stessa convenzione di denominazione?


9
C # non ha la thisparola chiave? Non potresti riferirti ai membri che usano this, come this.count?
FrustratedWithFormsDesigner

3
Il prefisso m_ è un C ++ - ism in cui evita conflitti di nomi tra campi e metodi. In C # questo non è un problema perché i nomi dei metodi dovrebbero iniziare con lettere maiuscole, campi con lettere minuscole.
amon,

4
Se stai stampando il codice nel 2017, stai facendo qualcosa di sbagliato. Negli altri due scenari, dovrebbe essere abbastanza ovvio che countnon è apparso dal nulla in quanto non è stato dichiarato nel metodo. Francamente l'argomento "fuori dall'IDE" è DAVVERO debole. Soprattutto perché ci sono persino strumenti di revisione del codice che funzionano nell'IDE.
Andy,

4
Post ancora rilevante di Joel .
Kasey Speakman,

Risposte:


19

Quando stavano lavorando sull'API per Windows, Microsoft raccomandava l'uso della Notazione ungherese, usando 'm_', così come 'i', 'sz', ecc. All'epoca, questo era utile perché l'IDE non era robusto abbastanza per mostrarti queste informazioni.

C #, d'altra parte, ha un IDE molto robusto fin dall'inizio.

Pertanto hanno formulato la raccomandazione nelle Linee guida per i nomi di Microsoft per C # per i campi pubblici statici o protetti:

 DO use PascalCasing in field names.
 DO name fields using a noun, noun phrase, or adjective.
X DO NOT use a prefix for field names.

Ora che puoi passarci sopra con il mouse o premere CTRL + K + W e ottenere tutte le informazioni sul membro, Microsoft ha deciso che i prefissi sono in cattive condizioni; sono una soluzione manuale a un problema già risolto.

I campi privati ​​sono specificamente esclusi dalle linee guida, ma Microsoft sembra essere giunto alla conclusione che questo è il caso anche per i campi privati ​​che vanno avanti internamente, che puoi vedere se punti Resharper su .NET 4.5 o .NET Core.

Usa i tuoi strumenti, non farlo manualmente.


2
Stessa risposta di Andy ... sì ma ... ci sono molte situazioni in cui sto guardando il codice al di fuori dell'IDE. Esempi - (1) unisci strumenti. (2) strumenti di revisione del codice. (3) stampe (sussulto).
Betty Crokker,

Andy ha pubblicato letteralmente nello stesso istante in cui l'ho fatto io. Ho visto apparire il "1 nuovo commento" mentre facevo clic su "aggiungi risposta". Per quanto riguarda quegli strumenti, due sono già nell'IDE. Stampe ... perché lo fai?
Kevin Fee,

1
La mia domanda iniziale rimane senza risposta ... sì, Microsoft ha detto di non usare m_ e i produttori di strumenti lo hanno seguito, ma non è un motivo tecnico per usare m_, è quello che viene spesso chiamato "appello all'autorità". L'unico motivo tecnico che ho visto per non usare m_ è venuto da Timothy Truckle e non lo capisco ...
Betty Crokker,

8
@BettyCrokker Perché m_ non aggiunge assolutamente alcun valore. L'IDE ti dice membro o no, quindi m_ è ridondante. Qual è il motivo per cui sei fissato su m_? Perché non l_ (per locale)? Perché non afsdfjdskfjas_? Ho intenzione di sottovalutare la domanda, perché è comunque una sciocca discussione religiosa. Fai quello che vuoi. Le linee guida che stai bussando dicono anche "Le linee guida per la denominazione dei campi si applicano ai campi pubblici e protetti statici . I campi interni e privati non sono coperti dalle linee guida , mentre i campi delle istanze pubbliche o protette non sono consentiti dalle linee guida per la progettazione dei membri."
Andy

1
@BettyCrokker OK, ma stai chiedendo delle linee guida che sono state fatte con tale presupposto. Ti stai anche lamentando di strumenti statici che probabilmente usano anche tale presupposto, perché "nessuno" (entro un margine statistico accettabile di errore) stampa il loro codice C #. Non so quali strumenti di analisi statistica usi, ma Resharper è piuttosto specifico: i campi privati ​​sono "_lowerCase". I membri pubblici di qualsiasi tipo o "UpperCase" e i locali sono "lowerCase". Il salvataggio sulla "m" in realtà sembra piuttosto carino, comunque.
Kevin Fee,

17

C # non ha variabili globali, quindi rimangono solo variabili locali e campi di classe.

E se hai così tante variabili locali che perdi la traccia del fatto che un identificatore sia uno, hai sicuramente violato una delle linee guida per gestire la complessità in modo più grave.

Prova a estrarre un oggetto-parametro, prova a dividere la tua funzione in più funzioni più semplici, sicuramente dovresti rifattare il tuo codice a meno che non ti piaccia annegare in minute e complessità, oltre a non avere alcun riguardo per la manutenibilità.

Ora che abbiamo stabilito che lo scopo delle decorazioni "this-is-a-member" non è più valido in quanto l'ambito delle funzioni dovrebbe essere troppo piccolo e l'ambito globale non esiste, c'è uno svantaggio di quegli indicatori: inibiscono spostando argomenti / variabili locali ai membri o viceversa.


Una volta puntato, potrebbe anche essere una classe o uno spazio dei nomi.
Codici A Caos il

3
Non penso che questo risponda davvero alla domanda.
Vladimir Stokic,

5
@FrankHileman In realtà lo è. Spiega perché non esiste una buona ragione per rendere un nome brutto.
Deduplicatore

2
"uglifying"? In questo caso, è solo una domanda. Tutti possono avere un'opinione diversa sulla bruttezza. Vi sono ragioni per preferire una convenzione rispetto a un'altra, ma in questo caso non esiste una convenzione standard.
Frank Hileman,

1
La bellezza di @Dupuplicator è negli occhi di chi guarda. Convenzioni che una volta consideravo brutte, ora apprezzo come utili.
Frank Hileman,

11

Perché gli sviluppatori evitano di anteporre spazi dei nomi con la direttiva using namespace?

System.DateTime()è molto più esplicito di DateTime(). Mi dice esattamente con cosa ho a che fare. È solo perché siamo dattilografi pigri?

No. Siamo dattilografi pigri, ma non è per questo.

Preferiamo stili di codifica che ci permettono di ignorare i dettagli e concentrarci sulle astrazioni. Costringere i nomi a comunicare i dettagli della struttura è fonte di distrazione. Quando lavoro su un algoritmo complicato non voglio nomi che insistano nel ricordarmi ogni piccolo dettaglio della cosa con cui sto lavorando. Ho solo bisogno del nome per impedirmi di confondere Timere DateTime. Mi preoccuperò se quelli sono compatibili altrove.

È la stessa ragione per cui non vuoi due ragazzi della tua squadra di nome Jon. Il fatto che abbiano cognomi non è un motivo per dare loro prefissi o suffissi. Ti chiamerò Skeet. Nulla di più è un dolore totale.


2
Questo non sembra rispondere alla domanda. Non esiste alcuna preferenza o convenzione generale per i campi privati ​​in .net.
Frank Hileman,

12
@FrankHileman In generale, preferiamo i buoni nomi. Le convenzioni non creano buoni nomi. Le convenzioni creano nomi come McOhPlease Von StopItAlreadyStine.
candied_orange,

Le convenzioni servono solo a semplificare il lavoro con codici di sviluppatori diversi. La tua risposta si rivolge ungherese, ma non esiste una convenzione per i campi privati, quindi la domanda si basa su un presupposto falso.
Frank Hileman,

7

Espandiamo un po '.

Esistono 3 stili di prefisso comuni, tutti occasionalmente rilevati nel codice c #:

  • m_
  • _
  • Questo.

Tali prefissi vengono comunemente utilizzati nell'implementazione privata di una classe . La raccomandazione di Microsoft riguarda principalmente le parti esposte di una classe.

Il vantaggio dell'utilizzo di m_over _è che il prefisso può essere lo stesso nell'intera base di codice se si utilizzano c # e le lingue in cui l'uso _come prefisso non è consentito . * Il vantaggio di m_over this.è che è più corto e che non accadrà accidentalmente dimentica il prefisso.

Il vantaggio di _over m_è che è più corto e che tutto ciò che inizia con il prefisso è ordinato in alto viene ordinato alfabeticamente da uno strumento. Il vantaggio di _over this.è che è più corto e che non dimenticherai accidentalmente il prefisso.

Il vantaggio di this.over m_ed _è che puoi usarlo in una base di codice dove _o m_non sono una convenzione accettata e universale. Ciò significa anche che nessuno ha bisogno di conoscere la convenzione _o m_, che può essere vista come una cosa più semplice. Come inconveniente, poiché il compilatore non si preoccupa di questo prefisso, a volte il this.prefisso mancherà e, in quanto tale, non sarà affidabile come un indicatore.


* Una volta che inizi ad accedere alle classi C # che usano _da C ++ / CLI (che in realtà non dovrebbero usare _), noterai che concordare un prefisso comune è più complesso di quanto dovrebbe essere. Decidere di non avere un prefisso elimina quel rumore. Combina questo con la risposta di Deduplicator, che parafraserò come "il vantaggio di un prefisso è quasi trascurabile", e ci dà: "C'è un piccolo problema quando si usano i prefissi, e un piccolo vantaggio, quindi è una scelta valida per non usali ".


C'è una domanda più vecchia su StackOverflow relativa a questo.


1
Bel confronto. Tuttavia, trovo che non usare alcun prefisso funzioni bene nella pratica.
Phil1970,

6

Va notato che la guida di stile MS parla solo di metodi e variabili pubblici e protetti. cioè gli altri sviluppatori vedranno se usano la tua libreria.

L'idea è che le librerie Microsoft abbiano un aspetto standard agli sviluppatori che le consumano.

Sei libero di usare qualunque stile ti piaccia sulle tue variabili private o locali.

Detto questo, i prefissi variabili sono scoraggiati in generale per una serie di ragioni

  • Possono essere sbagliati e quindi fuorvianti
  • Se stai aggiungendo il prefisso per tipo di variabile, non è chiaro come gestisci le classi che hai creato.
  • Esistono numerose convenzioni e non sempre sono d'accordo. Cosa trovi utile un altro sviluppatore potrebbe non essere utile
  • Nel caso delle variabili membro è possibile utilizzare questo "questo". parola chiave per indicare l'ambito e il complier lo imporrà.

2
Inizialmente non ho usato alcun prefisso per i campi. Successivamente, sono passato a utilizzare un singolo carattere "_", poiché ne vedevo altri utilizzarlo perché sposta tutti i campi in cima all'elenco alfabetico dei membri quando si utilizzano le caselle di riepilogo a discesa IDE. La mancanza di una convenzione standard può essere fastidiosa durante la lettura del codice scritto da molti sviluppatori diversi.
Frank Hileman,

dopo un po 'vedi tanti stili diversi che li ignori. Se provassi ad applicare uno stile, rifaresti continuamente tutto
Ewan,

4
Se applichi "Possono essere sbagliati, e quindi fuorvianti" per quasi tutto, scopri rapidamente che non dovresti nominare variabili o funzioni: potrebbero essere sbagliate dopo tutto! E suppongo che il tuo secondo punto elenco dovrebbe dire "classi che NON hai creato"? Altrimenti non lo capisco ... In ogni caso, inizia a sembrare che avere un singolo prefisso di sottolineatura per campi non pubblici sia uno standard non ufficiale, questa è probabilmente la direzione in cui andremo.
Betty Crokker,

alcune cose sono controllate dal compilatore, quindi m_count potrebbe non essere una variabile membro, ma this.count sarà sempre
Ewan

un sacco di ppl usa _ ma le cose cambiano nel tempo, scoprirai che il codice scritto per "best practice" l'anno scorso non corrisponde alle convenzioni di quest'anno
Ewan

4

Per me, se sto leggendo il codice di qualcun altro, e vedo questo:

count = 3;

Suppongo che 'count' sia una variabile locale per quella funzione e sto facendo qualcosa che verrà usato altrove nella funzione

E avrai perfettamente ragione se stai leggendo il codice sorgente che rispetta le convenzioni di stile di C #. In tale codice:

this.count = 3;

assegna un valore a un campo.

count = 3;

assegna un valore a una variabile locale.

Pertanto, le convenzioni di stile di C # sono chiare e inequivocabili. Ti costringono a non usare alcun prefisso semplicemente perché non ne hai bisogno.

Per quanto riguarda il codice sorgente per il quale gli autori hanno deciso di utilizzare invece le loro convenzioni personali, dovresti chiedere loro personalmente perché hanno fatto una scelta per farlo. Se non usano prefissi come caratteri di sottolineatura, mentre non usano this.nessuno dei due, ciò porterebbe effettivamente non solo a codici difficili da leggere, ma anche a casi in cui sarebbe necessaria una convenzione aggiuntiva:

public class Hello
{
    private string greetings;

    public Hello(string greetings)
    {
        greetings = greetings; // Wait, what is that?!
    }
}

C # non ha questa parola chiave? Non potresti riferirti ai membri che usano questo, come this.count?

Sì, ma (1) è più digitazione e (2) è facile da dimenticare. Se il campo si chiama m_count, non devo ricordare di digitare this.m_count

Qui, tuttavia, ti sbagli.

Scrive di più quando scrivi il tuo codice nel Blocco note. Con un IDE con completamento automatico o, meglio, IntelliSense, il confronto tra this.counte m_countnon è così ovvio. Nota il Shift+ -richiesto per digitare il carattere di sottolineatura.

Per quanto riguarda "facile da dimenticare", questo è rilevante solo per gli utenti di Blocco note. Non solo StyleCop e Resharper non ti faranno dimenticare di mettere this.quando necessario, ma dopo alcune ore di programmazione, mettere this.quando necessario diventa un'abitudine.


6
Trovo che l'uso thissolo quando ne hai bisogno si traduca in una sensazione davvero incoerente e mi distrae troppo spesso. Se hai intenzione di usare al thisposto di un prefisso, credo che dovresti sempre usarlo. In tal caso, diventa un prefisso e potresti anche usarlo _.
RubberDuck,

1
RubberDuck ha ragione. "Questo." è un prefisso, sebbene abbia un significato di compilatore.
Frank Hileman,

4

Il prefisso "membro" è una specie di dettaglio di implementazione. Distoglie l'attenzione dal dominio problematico al dominio della soluzione tecnica che distorce la mente quando si pensa a possibili rifatturazioni. - me

puoi spiegare ulteriormente? La tua è l'unica ragione per cui ho visto perché non usare il prefisso e non lo capisco ... (con questo intendo, le altre cose che la gente dice sono ragioni per cui non devo usarlo, che non è lo stesso del perché non dovrei) - Betty Crokker

Il mio punto è estendere le risposte di @CandiedOrange e @Ewan.

I prefissi rendono più difficile il refactoring

Man mano che il codice si evolve, variabili e metodi possono modificarne l'ambito. Per esempio. puoi creare un metodo privato e in seguito scoprire che dovrebbe essere disponibile anche per altre (nuove) classi. Simile può accadere ai parametri del metodo e alle variabili locali.

Supponiamo che tu abbia creato un calcolatore delle imposte. Secondo il principio dell'ambito di visibilità del leasing per le variabili, si inizia con un metodo che accetta sia l' aliquota fiscale che il valore base come parametri:
(l'esempio può sembrare Java-ish ...)

class TaxCalculator{
   double Calculate(double p_TaxRateInpercent, double p_BaseValue){
     return (100+p_TaxRateInpercent)/100 * p_BaseValue;
   }
} 

poi devi implementare l'operazione inversa e secondo TDD lo fai con il minimo sforzo:

class TaxCalculator{
   double Calculate(double p_TaxRateInpercent, double p_BaseValue){
     return (100+p_TaxRateInpercent)/100 * p_BaseValue;
   }
   double CalculateBase(double p_TaxRateInpercent, double p_TaxedValue){
     return p_TaxedValue/((100+p_TaxRateInpercent)/100);
   }
} 

Il prossimo TDD vuole che il refactoring del codice diventi più pulito, il che riduce l'elenco dei parametri di entrambi i metodi.
(Sì, per prima cosa estrarresti il ​​calcolo raddoppiato, ma fammi capire il mio punto ...)
La soluzione ovvia è cambiare l'aliquota fiscale in una variabile membro passandola come parametro del costruttore.

class TaxCalculator{
   double m_TaxRateInpercent;
   TaxCalculator(double p_TaxRateInpercent){
     m_TaxRateInpercent = p_TaxRateInpercent;
   }
   double Calculate(double p_BaseValue){
     return (100+m_TaxRateInpercent)/100 * p_BaseValue;
   }
   double CalculateBase(double p_TaxedValue){
     return p_TaxedValue/((100+m_TaxRateInpercent)/100);
   }
} 

Come puoi vedere, abbiamo dovuto modificare tutte le linee dei nostri metodi esistenti (qualsiasi linea che abbiamo usato p_TaxRateInpercentper essere esatta.

Il problema è che non hai il supporto del tuo IDE per eseguire la ridenominazione in tutta la classe. L'unico aiuto è la ricerca / sostituzione che cambierà anche il costruttore o qualsiasi parte che contenga accidentalmente la stringa p_TaxRateInpercent.
L'IDE potrebbe offrire una modifica a ... refactoring per nomi di variabili non definiti, ma questo potrebbe essere limitato all'ambito del metodo

Senza il prefisso sarebbero cambiate solo le firme del metodo. Non sarebbe stata necessaria alcuna ridenominazione.


I prefissi ingombrano la cronologia SCM quando vengono modificati

Inoltre, SCM registra la modifica del prefisso come modifiche nella logica sebbene la logica non sia cambiata! La storia di SCM è ingombra di questo cambiamento tecnico che nasconde ciò che è importante e aumenta il rischio di conflitti con i cambiamenti (logici reali) degli altri.


1

Uso il prefisso _ per tutte le variabili membro. Odio il prefisso "questo" perché, mentre alcuni sostengono che sia il modo giusto di fare le cose, aggiunge molto rumore / disordine alla base di codice. Un singolo trattino basso è anche una pratica comune negli esempi di Microsoft.

Quindi nel tuo esempio avrei:

int _count = 10;
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.