Quando usi la parola chiave “this”?

Ero curioso di sapere come le altre persone usano questa parola chiave. Tendo ad usarlo nei costruttori, ma posso anche usarlo in tutta la class in altri metodi. Qualche esempio:

In un costruttore:

public Light(Vector v) { this.dir = new Vector(v); } 

Altrove

 public void SomeMethod() { Vector vec = new Vector(); double d = (vec * vec) - (this.radius * this.radius); } 

Esistono diversi usi di questa parola chiave in C #.

  1. Per qualificare membri nascosti con un nome simile
  2. Per avere un object passare se stesso come parametro ad altri metodi
  3. Per fare in modo che un object si restituisca da un metodo
  4. Per dichiarare gli indicizzatori
  5. Per dichiarare i metodi di estensione
  6. Per passare i parametri tra costruttori
  7. Per riassegnare internamente il valore type (struct) value .
  8. Per richiamare un metodo di estensione sull’istanza corrente
  9. Per lanciarsi in un altro tipo
  10. Per catena costruttori definiti nella stessa class

È ansible evitare il primo utilizzo non avendo membri e variabili locali con lo stesso nome in ambito, ad esempio seguendo convenzioni di denominazione comuni e utilizzando le proprietà (caso Pascal) anziché i campi (caso cammello) per evitare la collisione con le variabili locali (anche cammello Astuccio). Nei campi C # 3.0 è ansible convertire facilmente le proprietà in proprietà utilizzando le proprietà implementate automaticamente .

Non intendo che questo suoni in modo allusivo, ma non importa.

Sul serio.

Guarda le cose importanti: il tuo progetto, il tuo codice, il tuo lavoro, la tua vita personale. Nessuno di loro avrà successo se non si utilizza la parola chiave “this” per qualificare l’accesso ai campi. Questa parola chiave non ti aiuterà a spedire in tempo. Non ridurrà i bug, non avrà alcun effetto apprezzabile sulla qualità del codice o sulla manutenibilità. Non ti farà ottenere un aumento o ti consente di trascorrere meno tempo in ufficio.

È davvero solo un problema di stile. Se ti piace “questo”, allora usalo. Se non lo fai, allora non farlo. Se ne hai bisogno per ottenere la semantica corretta, allora usalo. La verità è che ogni programmatore ha il suo stile di programmazione unico. Questo stile riflette le nozioni di quel particolare programmatore su come dovrebbe apparire il “codice più esteticamente gradevole”. Per definizione, qualsiasi altro programmatore che legge il tuo codice avrà uno stile di programmazione diverso. Ciò significa che ci sarà sempre qualcosa che l’altro non ha gradito o che avrebbe fatto diversamente. Ad un certo punto qualcuno leggerà il tuo codice e si lamenterà di qualcosa.

Non mi preoccuperei di questo. Vorrei solo assicurarmi che il codice sia il più gradevole ansible dal punto di vista estetico secondo i tuoi gusti. Se chiedi a 10 programmatori come formattare il codice, otterrai circa 15 opinioni diverse. Una cosa migliore su cui concentrarsi è come viene fattorizzato il codice. Le cose sono astratte, giusto? Ho scelto nomi significativi per le cose? C’è un sacco di duplicazione del codice? Ci sono modi in cui posso semplificare le cose? Ottenere le cose giuste, penso, avrà il maggior impatto positivo sul tuo progetto, sul tuo codice, sul tuo lavoro e sulla tua vita. Per coincidenza, probabilmente causerà anche l’altro a brontolare di meno. Se il tuo codice funziona, è facile da leggere ed è ben preso in considerazione, l’altro non scruterà come inizializzare i campi. Userà il tuo codice, si meraviglierà della sua grandezza e poi passerà a qualcos’altro.

Lo uso solo quando è assolutamente necessario, cioè quando un’altra variabile ne ombreggia un’altra. Come qui:

 class Vector3 { float x; float y; float z; public Vector3(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } } 

O come sottolinea Ryan Fox, quando è necessario passare questo parametro. (Le variabili locali hanno la precedenza sulle variabili membro)

Personalmente, cerco di usarlo sempre quando mi riferisco alle variabili membro. Aiuta a chiarire il codice e renderlo più leggibile. Anche se non c’è alcuna ambiguità, qualcuno che sta leggendo il mio codice per la prima volta non lo sa, ma se vedono questo usato in modo coerente, sapranno se stanno guardando una variabile membro o no.

Non posso credere che tutte le persone che dicono di usarlo siano sempre una “pratica migliore” e così via.

Usa “questo” quando c’è ambiguità, come nell’esempio di Corey o quando devi passare l’object come parametro, come nell’esempio di Ryan . Non c’è motivo di usarlo altrimenti perché essere in grado di risolvere una variabile basata sulla catena dell’ambito dovrebbe essere abbastanza chiaro che le variabili di qualificazione con essa non dovrebbero essere necessarie.

EDIT: La documentazione di C # su “this” indica un altro uso, oltre ai due che ho citato, per la parola chiave “this” – per dichiarare gli indicizzatori

EDIT: @Juan: Huh, non vedo alcuna incongruenza nelle mie dichiarazioni – ci sono 3 istanze in cui vorrei usare la parola chiave “this” (come documentato nella documentazione C #), e quelle sono le volte in cui ne hai effettivamente bisogno . Attaccare “questo” di fronte a variabili in un costruttore quando non c’è ombra che sta succedendo è semplicemente uno spreco di sequenze di tasti e uno spreco di tempo durante la lettura, non fornisce alcun beneficio.

Lo uso ogni volta che mi riferisco a una variabile di istanza, anche se non ne ho bisogno. Penso che renda il codice più chiaro.

Lo uso ogni volta che StyleCop mi dice di farlo. StyleCop deve essere rispettato. Oh si.

Ogni volta che hai bisogno di un riferimento all’object corrente.

Uno scenario particolarmente utile è quando il tuo object chiama una funzione e vuole passarci dentro.

Esempio:

 void onChange() { screen.draw(this); } 

Tendo ad usarlo ovunque, giusto per assicurarmi che sia chiaro che si tratta di membri di istanza con cui abbiamo a che fare.

Lo uso ovunque ci possa essere ambiguità (ovviamente). Non solo l’ambiguità del compilatore (sarebbe necessario in quel caso), ma anche l’ambiguità per chi guarda il codice.

Un altro uso piuttosto raro per questa parola chiave è quando è necessario richiamare un’implementazione esplicita dell’interfaccia dalla class di implementazione. Ecco un esempio forzato:

 class Example : ICloneable { private void CallClone() { object clone = ((ICloneable)this).Clone(); } object ICloneable.Clone() { throw new NotImplementedException(); } } 

Ecco quando lo uso:

  • Accesso ai metodi privati ​​all’interno della class (per differenziare)
  • Passare l’object corrente a un altro metodo (o come object mittente, in caso di un evento)
  • Quando si creano i metodi di estensione: D

Non lo uso per i campi Privati ​​perché prefisso i nomi delle variabili di campo private con un carattere di sottolineatura (_).

[C ++]

Sono d’accordo con “usalo quando devi” brigata. Decorare inutilmente il codice non è una buona idea, perché il compilatore non ti avviserà quando ti dimentichi di farlo. Ciò introduce una potenziale confusione per le persone che si aspettano che questo sia sempre lì, cioè dovranno pensarci .

Quindi, quando lo useresti? Ho appena dato un’occhiata al codice casuale e ho trovato questi esempi (non sto valutando se queste sono cose buone da fare o altro):

  • Passando “te stesso” a una funzione.
  • Assegnare “te stesso” a un puntatore o qualcosa del genere.
  • Casting, cioè lancio su / giù (sicuro o meno), costanza di lancio, ecc.
  • Disambiguazione forzata del compilatore.

Lo uso quando, in una funzione che accetta un riferimento a un object dello stesso tipo, voglio rendere perfettamente chiaro a quale object mi riferisco, dove.

Per esempio

 class AABB { // ... members bool intersects( AABB other ) { return other.left() < this->right() && this->left() < other.right() && // +y increases going down other.top() < this->bottom() && this->top() < other.bottom() ; } } ; 

(Vs)

 class AABB { bool intersects( AABB other ) { return other.left() < right() && left() < other.right() && // +y increases going down other.top() < bottom() && top() < other.bottom() ; } } ; 

A colpo d'occhio che AABB fa a right() riferiscono a? this aggiunge un po 'di chiarimento.

Nella risposta di Jakub Šturc, il suo numero 5 sul passaggio dei dati tra i controtettori probabilmente potrebbe usare una piccola spiegazione. Questo è in sovraccarico costruttori ed è l’unico caso in cui l’uso di this è obbligatorio. Nel seguente esempio possiamo chiamare il costruttore parametrico dal costruttore senza parametri con un parametro predefinito.

 class MyClass { private int _x public MyClass() : this(5) {} public MyClass(int v) { _x = v;} } 

Ho trovato che questa è una caratteristica particolarmente utile a volte.

Dovresti sempre usarlo, lo uso per diferantiare campi e parametri privati ​​(perché le nostre convenzioni di denominazione affermano che non usiamo prefissi per nomi di membri e parametri (e sono basati su informazioni trovate su Internet, quindi ritengo che la migliore pratica))

Ho preso l’abitudine di usarlo liberamente in Visual C ++, poiché così facendo avrei triggersto quelli di IntelliSense che ho premuto il tasto ‘>’, e sono pigro. (e incline agli errori di battitura)

Ma ho continuato a usarlo, dal momento che trovo utile vedere che sto chiamando una funzione membro piuttosto che una funzione globale.

Tendo a sottolineare i campi con _ quindi non ho mai veramente bisogno di usarlo. Anche R # tende a rifattorizzarli comunque …

Praticamente lo uso solo quando si fa riferimento a una proprietà type all’interno dello stesso tipo. Come menzionato da un altro utente, sottolineo anche i campi locali in modo che siano visibili senza bisogno di questo .

Lo uso solo quando richiesto, tranne che per operazioni simmetriche che a causa del polimorfismo a singolo argomento devono essere inserite in metodi di un lato:

 boolean sameValue (SomeNum other) { return this.importantValue == other.importantValue; } 

[C ++]

questo è usato nell’operatore di assegnazione dove la maggior parte del tempo è necessario controllare e prevenire strani (non intenzionali, pericolosi o solo una perdita di tempo per il programma) cose come:

 A a; a = a; 

Il tuo operatore di assegnazione sarà scritto:

 A& A::operator=(const A& a) { if (this == &a) return *this; // we know both sides of the = operator are different, do something... return *this; } 

this su un compilatore C ++

Il compilatore C ++ cercherà silenziosamente un simbolo se non lo trova immediatamente. A volte, la maggior parte delle volte, è buono:

  • utilizzando il metodo della class madre ‘se non lo si è sovraccaricato nella class figlio.
  • promuovere un valore di un tipo in un altro tipo

Ma a volte, semplicemente non vuoi che il compilatore indovini. Volete che il compilatore raccolga il simbolo giusto e non un altro.

Per me , quei tempi sono quando, all’interno di un metodo, voglio accedere a un metodo membro o variabile membro. Non voglio che un simbolo casuale venga preso solo perché ho scritto printf invece di print . this->printf non sarebbe stato compilato.

Il punto è che, con le librerie C legacy (§), il codice legacy scritto anni fa (§§), o qualunque cosa potrebbe accadere in una lingua in cui copia / incolla è una caratteristica obsoleta ma ancora triggers, a volte, dicendo al compilatore di non giocare l’intelligenza è una grande idea.

Queste sono le ragioni per cui lo uso.

(§) per me è ancora una specie di mistero, ma ora mi chiedo se il fatto che includi l’intestazione nella tua fonte, è la ragione per cui tutti i simboli legacy delle librerie C inquinano il tuo spazio dei nomi globale

(§§) rendendosi conto che “è necessario includere un’intestazione, ma che includendo questa intestazione interromperà il codice perché utilizza alcune macro stupide con un nome generico” è uno di quei momentjs della roulette russa della vita di un programmatore

Lo uso per invocare Intellisense proprio come JohnMcG , ma tornerò indietro e cancellerò “this->” quando ho finito. Seguo la convenzione Microsoft per il prefisso delle variabili membro con “m_”, quindi lasciandolo come documentazione sarebbe ridondante.

‘Questo.’ aiuta a trovare membri in questa “class” con molti membri (di solito a causa di una catena di ereditarietà profonda).

Colpire CTRL + Spazio non aiuta con questo, perché include anche i tipi; dove-come ‘questo.’ include membri SOLO.

Di solito lo cancello una volta che ho quello che cercavo: ma questo è solo il mio stile che sfonda.

In termini di stile, se sei un ranger solitario – decidi tu; se lavori per un’azienda mantieni la politica aziendale (guarda le cose nel controllo del codice sorgente e guarda cosa stanno facendo gli altri). In termini di utilizzo per qualificare i membri, nessuno dei due è giusto o sbagliato. L’unica cosa sbagliata è l’incoerenza: questa è la regola d’oro dello stile. Lascia perdere gli altri. Trascorri il tuo tempo meditando su problemi di codifica reali e, ovviamente, sulla programmazione.

Dipende dallo standard di codifica con cui lavoro. Se si utilizza _ per indicare una variabile di istanza, “questo” diventa ridondante. Se non stiamo usando _ allora tendo ad usarlo per indicare la variabile di istanza.

1 – Idem comune di Java setter:

  public void setFoo(int foo) { this.foo = foo; } 

2 – Quando si chiama una funzione con questo object come parametro

 notifier.addListener(this); 

Lo uso ogni volta che posso. Credo che renda il codice più leggibile e un codice più leggibile equivale a meno bug e più manutenibilità.

Quando ci sono molti sviluppatori che lavorano sulla stessa base di codice, hai bisogno di alcune linee guida / regole per il codice. Dove lavoro, abbiamo preferito utilizzare “questo” su campi, proprietà ed eventi.

Per me è logico farlo in questo modo, rende il codice più facile da leggere quando si fa una distinzione tra variabili di class e variabili di metodo.

C’è un uso che non è già stato menzionato in C ++, e che non è quello di riferirsi al proprio object o disambiguare un membro da una variabile ricevuta.

È ansible utilizzarlo per convertire un nome non dipendente in un nome dipendente dall’argomento all’interno di classi di modelli che ereditano da altri modelli.

 template  struct base { void f() {} }; template  struct derived : public base { void test() { //f(); // [1] error base::f(); // quite verbose if there is more than one argument, but valid this->f(); // f is now an argument dependent symbol } } 

I modelli sono compilati con un meccanismo a due passaggi. Durante il primo passaggio, solo i nomi dipendenti da non argomento vengono risolti e controllati, mentre i nomi dipendenti vengono controllati solo per coerenza, senza in realtà sostituire gli argomenti del modello.

In quel passo, senza effettivamente sostituire il tipo, il compilatore non ha quasi nessuna informazione su quale base potrebbe essere (notare che la specializzazione del modello base può trasformarlo in tipi completamente diversi, anche non definiti), quindi si presuppone che è un tipo In questa fase la chiamata f non dipendente che sembra naturale per il programmatore è un simbolo che il compilatore deve trovare come membro di derived o in spazi di nomi che racchiudono – cosa che non accade nell’esempio – e si lamenterà.

La soluzione sta trasformando il nome non dipendente f in un nome dipendente. Questo può essere fatto in un paio di modi, dichiarando esplicitamente il tipo in cui è implementato ( base::f –addoming the base rende il simbolo dipendente da T e il compilatore assumerà semplicemente che sarà esiste e rimanda il controllo effettivo per il secondo passaggio, dopo la sostituzione dell’argomento.

Il secondo modo, molto più complesso se si eredita da modelli che hanno più di un argomento, o nomi lunghi, è solo aggiungendo un this-> prima del simbolo. Siccome la class template che stai implementando dipende da un argomento (eredita dalla base ) this-> dipende dall’argomento, e otteniamo lo stesso risultato: this->f è controllato nel secondo round, dopo la sostituzione del parametro template .

Non dovresti usare “questo” a meno che tu non sia assolutamente necessario.

Esiste una penalità associata a verbosità non necessaria. Dovresti cercare un codice che sia esattamente il tempo necessario e non più.