Convenzione di denominazione: sottolineatura nelle variabili C ++ e C #


146

È comune vedere un _varnome di variabile in un campo di classe. Cosa significa il carattere di sottolineatura? C'è un riferimento per tutte queste speciali convenzioni di denominazione?



17
Alcune linee guida di codifica aziendale fuorvianti suggeriscono di aggiungere verruche alle variabili dei membri per distinguerle dalle variabili locali, per la convinzione che le classi e le funzioni cresceranno inevitabilmente così gonfie da non poter tenere traccia di ciò che è senza vaghi indizi come questo. Le persone che suggeriscono tali convenzioni sono troppo spesso quelle il cui codice ne ha più bisogno.
Mike Seymour,

26
@Mike - La tua affermazione generale non è vera. Ho scoperto che l'uso di questa convenzione semplifica notevolmente la scansione rapida dei metodi e una buona comprensione della classe.
ChaosPandion,

12
@ChaosPandion: quindi non leggerlo come un'istruzione coperta. "Alcuni" non significa "tutto" e "troppo spesso" non significa "sempre", e forse sei un'eccezione alla mia esperienza.
Mike Seymour,

17
In C ++, evitare i trattini bassi di punta . In molti contesti (vale a dire, nell'ambito globale, quando segue una lettera maiuscola, ecc.) Sono riservati per l'implementazione e si rischia effettivamente di avere qualche macro travolgere su di essi. Quindi, se li vuoi, rendili sottolineature finali .
sbi,

Risposte:


120

Il carattere di sottolineatura è semplicemente una convenzione; niente di più. Come tale, il suo uso è sempre in qualche modo diverso per ogni persona. Ecco come li capisco per le due lingue in questione:

In C ++, un carattere di sottolineatura di solito indica una variabile membro privata.

In C #, di solito vedo che viene utilizzato solo quando si definisce la variabile membro privata sottostante per una proprietà pubblica. Le altre variabili dei membri privati ​​non avrebbero un carattere di sottolineatura. Tuttavia, questo utilizzo è in gran parte al limite con l'avvento delle proprietà automatiche.

Prima:

private string _name;
public string Name
{
    get { return this._name; }
    set { this._name = value; }
}

Dopo:

public string Name { get; set; }

11
Ad eccezione dei team che desiderano che le loro proprietà siano immutabili.
ChaosPandion,

11
Gli identificatori che iniziano con i trattini bassi sono riservati al compilatore. L'uso del trattino di sottolineatura può essere in conflitto con i simboli del compilatore.
Thomas Matthews,

12
@Thomas Matthews - non in C #.
EMP,

11
@ChaosPandion Suppongo che ciò che intendi per "immutabile" sia in realtà "sola lettura", nel qual caso si può usare public string Name { get; private set; }. È vero, non è perfettamente immutabile, ma è lì.
jdmichal,

7
@Thomas In un contesto di classe, gli identificatori che iniziano con i trattini bassi seguiti da una lettera maiuscola sono riservati in C ++. _varnon è riservato.
Tyler McHenry,

84

È consigliabile NON utilizzare UNDERSCORES prima di qualsiasi nome di variabile o nome di parametro in C ++

I nomi che iniziano con un carattere di sottolineatura o un doppio carattere di sottolineatura sono RISERVATI per gli implementatori C ++. I nomi con un trattino basso sono riservati al funzionamento della libreria.

Se hai letto allo standard di codifica C ++, vedrai che nella prima pagina dice:

"Non ingigantire eccessivamente la denominazione, ma usa una convenzione di denominazione coerente: ci sono solo due cose da fare: a) non usare mai i" nomi subdoli ", quelli che iniziano con un carattere di sottolineatura o che contengono un doppio carattere di sottolineatura;" (p2, standard di codifica C ++, Herb Sutter e Andrei Alexandrescu)

Più specificamente, il progetto di lavoro ISO stabilisce le regole effettive:

Inoltre, alcuni identificatori sono riservati all'uso da parte delle implementazioni C ++ e non devono essere usati diversamente; non è richiesta alcuna diagnostica. (a) Ogni identificatore che contiene un carattere di sottolineatura doppio __ o inizia con un carattere di sottolineatura seguito da una lettera maiuscola è riservato all'implementazione per qualsiasi uso. (b) Ogni identificatore che inizia con un carattere di sottolineatura è riservato all'implementazione per l'uso come nome nello spazio dei nomi globale.

È consigliabile evitare di avviare un simbolo con un carattere di sottolineatura nel caso in cui si vaghi accidentalmente in una delle limitazioni di cui sopra.

Puoi vederlo da solo perché tale uso di caratteri di sottolineatura può essere disastroso durante lo sviluppo di un software:

Prova a compilare un semplice programma helloWorld.cpp come questo:

g++ -E helloWorld.cpp

Vedrai tutto ciò che accade in background. Ecco uno snippet:

   ios_base::iostate __err = ios_base::iostate(ios_base::goodbit);
   try
     {
       __streambuf_type* __sb = this->rdbuf();
       if (__sb)
  {
    if (__sb->pubsync() == -1)
      __err |= ios_base::badbit;
    else
      __ret = 0;
  }

Puoi vedere quanti nomi iniziano con un doppio trattino basso!

Inoltre, se guardi le funzioni dei membri virtuali, vedrai che * _vptr è il puntatore generato per la tabella virtuale che viene creato automaticamente quando usi una o più funzioni dei membri virtuali nella tua classe! Ma questa è un'altra storia ...

Se usi i caratteri di sottolineatura potresti avere problemi di conflitto e NON AVRAI IDEA che cosa lo sta causando, fino a quando non è troppo tardi.


4
I nomi che non hanno ambito globale o di file e iniziano con un trattino basso e una lettera minuscola sono perfetti? Lo faccio sempre perché è molto pulito per casi specifici, ad esempio: void A :: set_size (int _size) {size = _size; }. Cosa fai per usi banali come questo?
tukra,

3
Il carattere di sottolineatura seguito da una lettera minuscola è consentito in ambito non globale / file credo. Puoi mostrarmi dove nello standard dice che non lo è? Il problema con il carattere di sottolineatura finale è che lo uso già a volte per membri. Quindi potrei avere la funzione 'int size ();' membro var 'int size_;' e argomento 'int _size'. Questo tende a coprire bene le cose e non ho visto alternative migliori. Le funzioni in maiuscolo non sono possibili per i generici che funzionano con STL. Lo stile 'this.size' che piace alla gente di C # mi sembra soggetto a errori a meno che tu non lo usi sempre per l'accesso dei membri che è brutto.
tukra,

7
Capisco alcune persone che non vogliono usare il trattino basso e una lettera minuscola perché potrebbero avere difficoltà a chiarire ciò che è legale. Lo standard C ++ lo consente anche se alcuni autori raccomandano di non utilizzarlo. Per me, poiché è completamente legale e non riservato, non vedo alcun motivo per considerarlo rischioso. Grazie per il tuo feedback
tukra,

3
@tukra: non è completamente legale. Lo standard C ++ solo consente di utilizzare un singolo trattino basso seguito da una lettera minuscola in ambito locale. In bocca al lupo! :-)
Constantinos Glynos,

4
Scusate. Pensavo che avessimo bisogno di ripetere le qualifiche. Un identificatore C ++ che inizia con un carattere di sottolineatura seguito da una lettera minuscola è consentito in ogni ambito diverso dall'ambito del file. È sicuro e legale in funzioni, prototipi di funzioni, classi, strutture, sindacati, enum e tutto ciò che si inserisce in uno spazio dei nomi. Se segui la pratica comune di mettere tutto il tuo codice negli spazi dei nomi, allora sei completamente sicuro.
tukra,

45

In realtà la _varconvenzione viene da VB non C # o C ++ (m _, ... è un'altra cosa).

Questo è venuto a superare l'insensibilità del caso di VB nel dichiarare Proprietà.

Ad esempio, tale codice non è possibile in VB perché considera usere Usercome lo stesso identificatore

Private user As String

Public Property User As String
  Get
    Return user
  End Get
  Set(ByVal Value As String)
    user = value
  End Set
End Property

Quindi, per ovviare a questo, alcuni hanno usato una convenzione per aggiungere '_' al campo privato per arrivare in questo modo

Private _user As String

Public Property User As String
  Get
    Return _user
  End Get
  Set(ByVal Value As String)
    _user = value
  End Set
End Property

Poiché molte convenzioni sono per .Net e per mantenere una certa uniformità tra la convenzione C # e VB.NET, stanno usando la stessa.

Ho trovato il riferimento per quello che stavo dicendo: http://10rem.net/articles/net-naming-conventions-and-programming-standards---best-practices

Custodia per cammello con trattino di punta. In VB.NET, indicare sempre "Protetto" o "Privato", non utilizzare "Dim". L'uso di "m_" è sconsigliato, così come l'uso di un nome di variabile che si differenzia dalla proprietà solo per caso, in particolare con variabili protette in quanto viola la conformità e renderà la tua vita un problema se programmi in VB.NET, come dovrebbe nominare i tuoi membri in modo diverso dalle proprietà accessor / mutator. Di tutti gli elementi qui, il carattere di sottolineatura principale è davvero l'unico controverso. Personalmente lo preferisco al semplice caso di cammello senza sottolineature per le mie variabili private in modo da non dover qualificare i nomi delle variabili con "questo". distinguere dai parametri nei costruttori o altrove dove probabilmente avrò una collisione di denominazione. Con la distinzione tra maiuscole e minuscole di VB.NET, questo è ancora più importante in quanto le proprietà degli accessor avranno di solito lo stesso nome delle variabili dei membri privati ​​ad eccezione del carattere di sottolineatura. Per quanto riguarda m_, si tratta davvero solo di estetica. Io (e molti altri) trovo m_ brutto, in quanto sembra che ci sia un buco nel nome della variabile. È quasi offensivo. Lo usavo sempre in VB6, ma solo perché le variabili non potevano avere un carattere di sottolineatura iniziale. Non potrei essere più felice di vederlo andare via. Microsoft consiglia contro m_ (e il diritto _) anche se hanno fatto entrambi nel loro codice. Inoltre, il prefisso con una "m" dritta è subito chiaro. Naturalmente, poiché codificano principalmente in C #, possono avere membri privati ​​che differiscono solo nel caso dalle proprietà. La gente di VB deve fare qualcos'altro. Anziché provare a presentare casi speciali lingua per lingua, raccomando il carattere di sottolineatura principale per tutte le lingue che lo supporteranno. Se voglio che la mia classe sia completamente conforme a CLS, potrei lasciare il prefisso su qualsiasi variabile membro protetta da C #. In pratica, tuttavia, non mi preoccupo mai di questo perché mantengo private tutte le variabili membro potenzialmente protette e fornisco invece accessori e mutatori protetti. Perché: in poche parole, questa convenzione è semplice (un personaggio), facile da leggere (l'occhio non viene distratto da altri personaggi principali) ed evita con successo la collisione dei nomi con variabili a livello di procedura e proprietà a livello di classe. Proprietà a livello di classe . Consiglio il carattere di sottolineatura principale per tutte le lingue che lo supporteranno. Se voglio che la mia classe sia completamente conforme a CLS, potrei lasciare il prefisso su qualsiasi variabile membro protetta da C #. In pratica, tuttavia, non mi preoccupo mai di questo perché mantengo private tutte le variabili membro potenzialmente protette e fornisco invece accessori e mutatori protetti. Perché: in poche parole, questa convenzione è semplice (un personaggio), facile da leggere (l'occhio non viene distratto da altri personaggi principali) ed evita con successo la collisione dei nomi con variabili a livello di procedura e proprietà a livello di classe. Proprietà a livello di classe . Consiglio il carattere di sottolineatura principale per tutte le lingue che lo supporteranno. Se voglio che la mia classe sia completamente conforme a CLS, potrei lasciare il prefisso su qualsiasi variabile membro protetta da C #. In pratica, tuttavia, non mi preoccupo mai di questo, poiché mantengo private tutte le variabili membro potenzialmente protette e fornisco invece accessori e mutatori protetti. Perché: in poche parole, questa convenzione è semplice (un personaggio), facile da leggere (l'occhio non viene distratto da altri personaggi principali) ed evita con successo la collisione dei nomi con variabili a livello di procedura e proprietà a livello di classe. Proprietà a livello di classe . Non mi preoccupo mai di questo perché mantengo private tutte le variabili membro potenzialmente protette e fornisco invece accessori e mutatori protetti. Perché: in poche parole, questa convenzione è semplice (un personaggio), facile da leggere (l'occhio non viene distratto da altri personaggi principali) ed evita con successo la collisione dei nomi con variabili a livello di procedura e proprietà a livello di classe. Proprietà a livello di classe . Non mi preoccupo mai di questo perché mantengo private tutte le variabili membro potenzialmente protette e fornisco invece accessori e mutatori protetti. Perché: in poche parole, questa convenzione è semplice (un personaggio), facile da leggere (l'occhio non viene distratto da altri personaggi principali) ed evita con successo la collisione dei nomi con variabili a livello di procedura e proprietà a livello di classe. Proprietà a livello di classe .


6

Il primo commentatore (R Samuel Klatchko) ha fatto riferimento: Quali sono le regole sull'uso di un trattino basso in un identificatore C ++? che risponde alla domanda sul carattere di sottolineatura in C ++. In generale, non è necessario utilizzare un carattere di sottolineatura iniziale, poiché è riservato all'implementatore del compilatore. Il codice con cui stai vedendo _varè probabilmente un codice legacy o un codice scritto da qualcuno che è cresciuto usando il vecchio sistema di denominazione che non si è accigliato con i caratteri di sottolineatura principali.

Come indicato da altre risposte, era usato in C ++ per identificare le variabili dei membri della classe. Tuttavia, non ha alcun significato speciale per quanto riguarda decoratori o sintassi. Quindi, se si desidera utilizzarlo, verrà compilato.

Lascio la discussione in C # ad altri.


5

_var non ha alcun significato e serve solo allo scopo di semplificare la distinzione che la variabile è una variabile membro privata.

In C ++, l'uso della convenzione _var non è corretto, poiché esistono regole che governano l'uso del trattino basso davanti a un identificatore. _var è riservato come identificatore globale, mentre _Var (trattino basso + lettera maiuscola) è sempre riservato. Questo è il motivo per cui in C ++ vedrai invece persone che usano la convenzione var_.


4

Puoi creare le tue linee guida per la codifica. Basta scrivere una documentazione chiara per il resto della squadra.

L'uso di _field aiuta Intelilsense a filtrare tutte le variabili di classe semplicemente digitando _.

Di solito seguo le Linee guida di Brad Adams , ma mi consiglia di non utilizzare il carattere di sottolineatura.


2

Lo standard di denominazione Microsoft per C #, dice variabili e parametri devono utilizzare il modulo caso cammello inferiore IE: paramName . Lo standard prevede inoltre campi per seguire la stessa forma, ma questo può portare a codice chiaro così tante squadre richiedono un prefisso di sottolineatura per migliorare la chiarezza IE: _fieldName .


2

Con C #, le Linee guida per la progettazione di Microsoft Framework suggeriscono di non utilizzare il carattere di sottolineatura per i membri pubblici . Per i membri privati , i caratteri di sottolineatura sono OK da usare. In effetti, Jeffrey Richter (spesso citato nelle linee guida) usa un m_ per esempio e un "s_" per membri statici privati.

Personalmente, uso solo _ per contrassegnare i miei membri privati. "m_" e "s_" si avvicinano alla notazione ungherese che non è solo disapprovata in .NET, ma può essere piuttosto dettagliata e trovo che le classi con molti membri siano difficili da eseguire una rapida occhiata in ordine alfabetico (immagina 10 variabili tutte che iniziano con m_) .


Dal momento che la notazione ungherese indica il tipo, non penso che si possa davvero dire che m / s al limite.
Jerry Nixon,

@ JerryNixon-MSFT Identifichiamo il tipo di dati variabili e il tipo di visibilità variabile come la stessa cosa quando si tratta della nozione ungherese?
Necro,

1

Uso il nome _var per le variabili membro delle mie classi. Ci sono 2 motivi principali per cui lo faccio:

1) Mi aiuta a tenere traccia delle variabili di classe e delle variabili di funzione locali quando leggo il mio codice in seguito.

2) Aiuta Intellisense (o altro sistema di completamento del codice) quando cerco una variabile di classe. Il solo fatto di conoscere il primo carattere è utile per filtrare l'elenco delle variabili e dei metodi disponibili.


1
È una seccatura in un metodo più grande fare affidamento su hover intellisense o scansione per vedere se un var è definito localmente o no. Preferisci anche "__" per la statica per le stesse ragioni che menzioni ma al momento non è favorevole.
crokusek,

1

Per quanto riguarda i linguaggi C e C ++, non esiste un significato speciale per un carattere di sottolineatura nel nome (inizio, metà o fine). È solo un carattere di nome variabile valido. Le "convenzioni" provengono da pratiche di codifica all'interno di una comunità di codifica.

Come già indicato da vari esempi sopra, _ all'inizio può significare membri privati ​​o protetti di una classe in C ++.

Vorrei solo dare un po 'di storia che può essere divertente curiosità. In UNIX se si dispone di una funzione di libreria C centrale e di un back-end del kernel in cui si desidera esporre la funzione del kernel allo spazio utente, _ è bloccato di fronte allo stub della funzione che chiama direttamente la funzione del kernel senza fare altro. L'esempio più famoso e familiare di questo è exit () vs _exit () nei kernel di tipo BSD e SysV: lì, exit () fa cose nello spazio utente prima di chiamare il servizio di uscita del kernel, mentre _exit si limita al servizio di uscita del kernel.

Quindi _ è stato usato per cose "locali" in questo caso locale essendo macchina locale. In genere _functions () non erano portatili. In questo non dovresti aspettarti lo stesso comportamento su varie piattaforme.

Ora come per _ nei nomi delle variabili, come ad esempio

int _foo;

Bene psicologicamente, un _ è una cosa strana da digitare all'inizio. Quindi, se si desidera creare un nome di variabile che avrebbe una minore possibilità di scontro con qualcos'altro, in particolare quando si ha a che fare con sostituzioni pre-processore si considera l'uso di _.

Il mio consiglio di base sarebbe di seguire sempre le convenzioni della tua comunità di programmazione, in modo da poter collaborare in modo più efficace.


0

Significa semplicemente che è un campo membro nella classe.


0

Non esiste una particolare convenzione di denominazione singola, ma l'ho vista per i membri privati.


0

A molte persone piace avere campi privati ​​con il prefisso. È solo una convenzione di denominazione.

Le convenzioni di denominazione "ufficiali" di C # prescrivono nomi minuscoli semplici (senza trattino basso) per i campi privati.

Non sono a conoscenza delle convenzioni standard per il C ++, sebbene i caratteri di sottolineatura siano ampiamente utilizzati.


0

È solo una convenzione che alcuni programmatori usano per chiarire quando manipoli un membro della classe o qualche altro tipo di variabile (parametri, locale alla funzione, ecc.). Un'altra convenzione ampiamente utilizzata per le variabili membro è il prefisso del nome con "m_".

Comunque, queste sono solo convenzioni e non troverai un'unica fonte per tutte. Sono una questione di stile e ogni team di programmazione, progetto o azienda ha il suo (o addirittura non ne ha).


0

C'è un motivo pienamente legittimo per usarlo in C #: se il codice deve essere estendibile anche da VB.NET. (Altrimenti, non lo farei.)

Poiché VB.NET non fa distinzione tra maiuscole e minuscole, non esiste un modo semplice per accedere al fieldmembro protetto in questo codice:

public class CSharpClass
{
    protected int field;
    public int Field { get { return field; } }
}

Ad esempio, questo accederà al getter della proprietà, non al campo:

Public Class VBClass
    Inherits CSharpClass

    Function Test() As Integer
        Return Field
    End Function

End Class

Cavolo, non riesco nemmeno a scrivere fieldin minuscolo - VS 2010 continua a correggerlo.

Per renderlo facilmente accessibile alle classi derivate in VB.NET, è necessario elaborare un'altra convenzione di denominazione. Il prefisso di un trattino basso è probabilmente il meno invadente e il più "storicamente accettato".


0

Ora la notazione che usa "this" come in this.foobarbaz è accettabile per le variabili membro della classe C #. Sostituisce la vecchia notazione "m_" o solo "__". Rende il codice più leggibile perché non vi è alcun dubbio su cosa si sta facendo riferimento.


0

Dalla mia esperienza (sicuramente limitata), un trattino basso indicherà che si tratta di una variabile membro privata. Come ha detto Gollum, questo dipenderà dalla squadra, comunque.


0

Vecchia domanda, nuova risposta (C #).

Un altro uso di caratteri di sottolineatura per C # è con DI DI NET NET (iniezione di dipendenza). Le readonlyvariabili private di una classe assegnate all'interfaccia iniettata durante la costruzione dovrebbero iniziare con un trattino basso. Immagino sia un dibattito se usare il carattere di sottolineatura per ogni membro privato di una classe (sebbene la stessa Microsoft lo segua) ma questo è certo.

private readonly ILogger<MyDependency> _logger;

public MyDependency(ILogger<MyDependency> logger)
{
    _logger = logger;
}

-2

Una convenzione di denominazione come questa è utile quando leggi il codice, in particolare il codice che non è il tuo. Una forte convenzione di denominazione aiuta a indicare dove viene definito un determinato membro, che tipo di membro è, ecc. La maggior parte dei team di sviluppo adotta una semplice convenzione di denominazione e semplicemente prefigura i campi dei membri con un carattere di sottolineatura ( _fieldName). In passato, ho usato la seguente convenzione di denominazione per C # (che si basa sulle convenzioni di Microsofts per il codice di .NET framework, che può essere visto con Reflector):

Campo istanza: m_fieldName
Campo statico: s_fieldName Membro
pubblico / protetto / interno: PascalCasedName ()
Membro privato: camelCasedName ()

Questo aiuta le persone a comprendere la struttura, l'uso, l'accessibilità e la posizione dei membri quando leggono il codice sconosciuto molto rapidamente.


3
In realtà, Microsoft non consiglia di utilizzare i prefissi m_, s_. Non utilizzare un prefisso per i nomi dei campi. Ad esempio, non utilizzare g_ o s_ per distinguere i campi statici da quelli non statici. msdn.microsoft.com/en-us/library/ms229012.aspx
Zied,

1
Come ho detto, dai un'occhiata al loro codice sorgente con Reflector. Sarai sorpreso di quanto si sottraggano ai loro stessi consigli. ;) Come menzionato nel mio post, aiuta a migliorare la leggibilità del tuo codice e al mio ultimo team è piaciuta molto la convenzione di denominazione sopra elencata.
jrista,
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.