Qual è lo scopo di nameof?


264

La versione 6.0 ha una nuova funzionalità di nameof, ma non riesco a capirne lo scopo, in quanto prende semplicemente il nome della variabile e lo cambia in una stringa durante la compilazione.

Ho pensato che potesse avere qualche scopo durante l'utilizzo, <T>ma quando ci provo nameof(T)mi stampa solo un tipo Tinvece del tipo usato.

Qualche idea sullo scopo?



28
Non c'era modo di farlo Tprima. C'era un modo per ottenere il tipo usato prima.
Jon Hanna,

Inizialmente sembrava eccessivo e non vedo ancora un motivo convincente per usarlo. Forse un esempio MVC?
Corey Alix,

15
Decisamente utile quando si refact / rinomina il nome all'interno nameof. Aiuta anche a prevenire errori di battitura.
bvj,

4
La documentazione ufficiale di nameof è stata spostata qui: docs.microsoft.com/en-us/dotnet/csharp/language-reference/… - Elenca anche i casi d'uso chiave che servono come una buona risposta alla domanda.
markus s

Risposte:


324

Che dire dei casi in cui si desidera riutilizzare il nome di una proprietà, ad esempio quando si genera un'eccezione in base al nome di una proprietà o si gestisce un PropertyChangedevento. Esistono numerosi casi in cui si desidera avere il nome della proprietà.

Prendi questo esempio:

switch (e.PropertyName)
{
    case nameof(SomeProperty):
    { break; }

    // opposed to
    case "SomeOtherProperty":
    { break; }
}

Nel primo caso, anche la ridenominazione SomePropertymodificherà il nome della proprietà o interromperà la compilazione. L'ultimo caso no.

Questo è un modo molto utile per mantenere la compilazione del codice e liberare i bug (sorta di).

(Un bellissimo articolo di Eric Lippert perché infoofnon ce l'ha fatta, mentre l'ha nameoffatto)


2
Capisco il punto, aggiungendo semplicemente che il resharper modifica le stringhe durante il refactoring dei nomi, non sono sicuro che VS abbia funzionalità simili.
Ash Burlaczenko,

8
Esso ha. Ma sia Resharper che VS non funzionano su progetti per esempio. Questo fa. In effetti, questa è la soluzione migliore.
Patrick Hofman,

50
Un altro caso d'uso comune è il routing in MVC utilizzando nameofe il nome dell'azione anziché una stringa codificata.
RJ Cuthbertson,

2
@sotn Non sono sicuro di aver capito cosa stai chiedendo. Non c'è nulla che ti impedisca di usarlo in questo modo public class MyController { public ActionResult Index() { return View(nameof(Index)); } }- e puoi usarlo nameofsu membri non statici (ad esempio puoi chiamare nameof(MyController.Index)usando la classe sopra ed emetterà "Index"). Dai un'occhiata agli esempi su msdn.microsoft.com/en-us/library/…
RJ Cuthbertson

2
Non vedo perché sia ​​speciale. I nomi delle variabili sono sempre gli stessi, giusto? Che tu abbia un'istanza o meno, i nomi delle variabili non cambieranno @sotn
Patrick Hofman,

177

È davvero utile per i ArgumentExceptionsuoi derivati:

public string DoSomething(string input) 
{
    if(input == null) 
    {
        throw new ArgumentNullException(nameof(input));
    }
    ...

Ora, se qualcuno refactoring il nome del inputparametro, anche l'eccezione verrà mantenuta aggiornata.

È anche utile in alcuni luoghi in cui in precedenza si doveva usare la riflessione per ottenere i nomi di proprietà o parametri.

Nel tuo esempio nameof(T) ottiene il nome del parametro type - anche questo può essere utile:

throw new ArgumentException(nameof(T), $"Type {typeof(T)} does not support this method.");

Un altro uso di nameof è per enum - di solito se vuoi il nome di stringa di un enum che usi .ToString():

enum MyEnum { ... FooBar = 7 ... }

Console.WriteLine(MyEnum.FooBar.ToString());

> "FooBar"

Questo è in realtà relativamente lento in quanto .Net contiene il valore enum (cioè 7) e trova il nome in fase di esecuzione.

Invece usa nameof :

Console.WriteLine(nameof(MyEnum.FooBar))

> "FooBar"

Ora .Net sostituisce il nome enum con una stringa in fase di compilazione.


Ancora un altro uso è per cose come INotifyPropertyChanged e la registrazione - in entrambi i casi si desidera che il nome del membro che si sta chiamando venga passato a un altro metodo:

// Property with notify of change
public int Foo
{
    get { return this.foo; }
    set
    {
        this.foo = value;
        PropertyChanged(this, new PropertyChangedEventArgs(nameof(this.Foo));
    }
}

O...

// Write a log, audit or trace for the method called
void DoSomething(... params ...)
{
    Log(nameof(DoSomething), "Message....");
}

9
E hai messo un'altra caratteristica interessante in: interpolazione di stringhe!
Patrick Hofman,

1
@PatrickHofman e typeof(T), che è un altro pezzo di zucchero in fase di compilazione che è utile in circostanze simili :-)
Keith

1
Una cosa che mi manca è qualcosa del genere nameofthismethod. Puoi usarlo Log.Error($"Error in {nameof(DoSomething)}...")ma se lo incolli copiando in altri metodi non noterai che si sta ancora riferendo DoSomething. Quindi, mentre funziona perfettamente con variabili o parametri locali, il metodo-nome è un problema.
Tim Schmelter,

3
Sai se nameOfutilizzerà l' [DisplayName]attributo se presente? Per l' enumesempio che uso [DisplayName]spesso con progetti MVC
Luke T O'Brien,

2
@AaronLS Sì, è abbastanza specializzato e non qualcosa che useresti spesso. Tuttavia, si throw newtratta di un altro anti-pattern: trovo che un uso eccessivo catchsia un problema comune con gli sviluppatori junior perché sembra risolvere il problema (quando la maggior parte delle volte lo nasconde).
Keith

26

Un altro caso d'uso in cui la nameoffunzionalità di C # 6.0 diventa utile : considera una libreria come Dapper che semplifica il recupero dei DB. Anche se questa è un'ottima libreria, è necessario codificare i nomi di proprietà / campi all'interno della query. Ciò significa che se decidi di rinominare la proprietà / il campo, è molto probabile che dimenticherai di aggiornare la query per utilizzare i nuovi nomi di campo. Con l'interpolazione e le nameoffunzionalità delle stringhe , il codice diventa molto più facile da mantenere e da scrivere.

Dall'esempio fornito nel link

senza nome di

var dog = connection.Query<Dog>("select Age = @Age, Id = @Id", new { Age = (int?)null, Id = guid });

con il nome di

var dog = connection.Query<Dog>($"select {nameof(Dog.Age)} = @Age, {nameof(Dog.Id)} = @Id", new { Age = (int?)null, Id = guid });

3
Adoro Dapper e mi piacciono molto le interporlazioni di stringhe, ma IMO sembra così brutto. Il rischio di interrompere una query rinominando una colonna sembra così piccolo rispetto a query così brutte. A prima vista direi che preferisco scrivere query EF LINQ o seguire una convenzione come [TableName]. [ColumnName] dove posso facilmente trovare / sostituire le mie query quando ne ho bisogno.
drizin,

@drizin Uso Dapper FluentMap per prevenire tali domande (e anche per la separazione delle preoccupazioni)
mamuesstack

21

La tua domanda esprime già lo scopo. Devi vedere che questo potrebbe essere utile per la registrazione o il lancio di eccezioni.

per esempio.

public void DoStuff(object input)
{
    if (input == null)
    {
        throw new ArgumentNullException(nameof(input));
    }
}

questo va bene, se cambio il nome della variabile il codice si romperà invece o restituirà un'eccezione con un messaggio errato .


Naturalmente, gli usi non si limitano a questa semplice situazione. Puoi usarlo nameofogni volta che sarebbe utile codificare il nome di una variabile o proprietà.

Gli usi sono molteplici se si considerano varie situazioni di rilegatura e riflessione. È un modo eccellente per portare gli errori di runtime alla compilazione del tempo.


6
@atikot: Ma se si rinomina la variabile, il compilatore non noterà più che la stringa non corrisponde più.
OR Mapper,

1
In realtà uso il resharper che lo tiene in considerazione, ma vedo il tuo punto.
atikot,

4
@atikot, anche io, ma Resharper genera solo un avviso, non un errore del compilatore. C'è una differenza tra una certezza e un buon consiglio.
Jodrell,

1
@atikot e, Resharper non controlla i messaggi di log
Jodrell,

2
@Jodrell: E, sospetto, non controlla nemmeno altri usi - che ne pensi dei bind WPF creati in code-behind, dei OnPropertyChangedmetodi personalizzati (che accettano direttamente i nomi delle proprietà anziché PropertyChangedEventArgs), o delle chiamate a reflection per cercare un particolare membro o tipo?
OR Mapper,

13

Il caso d'uso più comune che mi viene in mente è quando si lavora con l' INotifyPropertyChangedinterfaccia. (Fondamentalmente tutto ciò che riguarda WPF e associazioni utilizza questa interfaccia)

Dai un'occhiata a questo esempio:

public class Model : INotifyPropertyChanged
{
    // From the INotifyPropertyChanged interface
    public event PropertyChangedEventHandler PropertyChanged;

    private string foo;
    public String Foo
    {
        get { return this.foo; }
        set
        {
            this.foo = value;
            // Old code:
            PropertyChanged(this, new PropertyChangedEventArgs("Foo"));

            // New Code:
            PropertyChanged(this, new PropertyChangedEventArgs(nameof(Foo)));           
        }
    }
}

Come puoi vedere nel vecchio modo, dobbiamo passare una stringa per indicare quale proprietà è cambiata. Con nameofpossiamo usare direttamente il nome della proprietà. Questo potrebbe non sembrare un grosso problema. Ma immagina cosa succede quando qualcuno cambia il nome della proprietàFoo . Quando si utilizza una stringa, l'associazione smetterà di funzionare, ma il compilatore non ti avviserà. Quando si utilizza nameof viene visualizzato un errore del compilatore che non contiene proprietà / argomento con il nome Foo.

Si noti che alcuni framework usano un po 'di magia di riflessione per ottenere il nome della proprietà, ma ora abbiamo il nome di questo non è più necessario .


5
Sebbene si tratti di un approccio valido, un approccio più conveniente (e DRY) consiste nell'utilizzare l' [CallerMemberName]attributo sul parametro di un nuovo metodo per generare questo evento.
Drew Noakes,

1
Sono d'accordo che anche CallerMemberName sia carino, ma è un caso d'uso separato, poiché (come hai detto) puoi usarlo solo nei metodi. Per quanto riguarda DRY, non sono sicuro che [CallerMemberName]string x = nullsia meglio di nameof(Property). Si potrebbe dire che il nome della proprietà viene utilizzato due volte, ma in pratica si tratta dell'argomento passato a una funzione. Non proprio quello che si intende con DRY credo :).
Roy T.

In realtà puoi usarlo nelle proprietà. Anche loro sono membri. Il vantaggio nameofè che il setter di proprietà non ha bisogno di specificare il nome della proprietà, eliminando la possibilità di copiare / incollare bug.
Ha disegnato Noakes il

4
È una situazione "migliore insieme", poiché l' INotifyPropertyChangedutilizzo di [CallerMemberNameAttribute]consente di inoltrare in modo chiaro la notifica di modifica da un setter di proprietà, mentre la nameofsintassi consente di inoltrare in modo pulito una notifica di modifica da una posizione diversa nel codice.
Andrew Hanlon,

9

L'utilizzo più comune sarà nella convalida dell'input, ad esempio

//Currently
void Foo(string par) {
   if (par == null) throw new ArgumentNullException("par");
}

//C# 6 nameof
void Foo(string par) {
   if (par == null) throw new ArgumentNullException(nameof(par));
}

Nel primo caso, se si refactoring il metodo che modifica il nome del parametro par , probabilmente si dimenticherà di modificarlo in ArgumentNullException . Con il nome non devi preoccupartene.

Vedi anche: nameof (C # e Visual Basic Reference)


7

Il progetto ASP.NET Core MVC utilizza nameofnel AccountController.cse ManageController.cscon il RedirectToActionmetodo per fare riferimento a un'azione nel controller.

Esempio:

return RedirectToAction(nameof(HomeController.Index), "Home");

Questo si traduce in:

return RedirectToAction("Index", "Home");

e porta l'utente all'azione "Indice" nel controller "Home", ad es /Home/Index.


Perché non fare il maiale e usare tutto return RedirectToAction(nameof(HomeController.Index), nameof(HomeController).Substring(nameof(HomeController),0,nameof(HomeController).Length-"Controller".Length));?
Suncat2000,

@ Suncat2000 perché una di queste cose viene eseguita in compilation e l'altra no? :)
Dinerdo,

6

Come altri hanno già sottolineato, il nameof operatore inserisce il nome assegnato all'elemento nel codice sorgente.

Vorrei aggiungere che questa è davvero una buona idea in termini di refactoring poiché rende sicuro questo refactoring di stringa. In precedenza, ho usato un metodo statico che utilizzava la riflessione per lo stesso scopo, ma che ha un impatto sulle prestazioni di runtime. L' nameofoperatore non ha alcun impatto sulle prestazioni di runtime; fa il suo lavoro in fase di compilazione. Se dai un'occhiata al MSILcodice troverai la stringa incorporata. Vedi il metodo seguente e il suo codice smontato.

static void Main(string[] args)
{
    Console.WriteLine(nameof(args));
    Console.WriteLine("regular text");
}

// striped nops from the listing
IL_0001 ldstr args
IL_0006 call System.Void System.Console::WriteLine(System.String)
IL_000C ldstr regular text
IL_0011 call System.Void System.Console::WriteLine(System.String)
IL_0017 ret

Tuttavia, questo può essere uno svantaggio se si prevede di offuscare il software. Dopo l'offuscamento, la stringa incorporata potrebbe non corrispondere più al nome dell'elemento. I meccanismi che si basano su questo testo si romperanno. Esempi per questo, inclusi ma non limitati a: Riflessione, NotifyPropertyChanged ...

Determinare il nome durante il runtime costa alcune prestazioni, ma è sicuro per l'offuscamento. Se l'offuscamento non è né richiesto né pianificato, consiglierei di utilizzare l' nameofoperatore.


5

Considera che usi una variabile nel tuo codice e devi ottenere il nome della variabile e diciamo di stamparla, dovresti usare

int myVar = 10;
print("myVar" + " value is " + myVar.toString());

E se qualcuno refactifica il codice e usa un altro nome per "myVar", dovrebbe cercare il valore di stringa nel codice e modificarlo di conseguenza.

Invece se tu avessi

print(nameof(myVar) + " value is " + myVar.toString());

Aiuterebbe a refactoring automaticamente!


Vorrei che ci fosse stata una speciale sintassi a parametro variabile che avrebbe passato una matrice di tuple, una per ogni parametro, contenente la rappresentazione del codice sorgente, il Typee il valore. Ciò consentirebbe ai codici che invocano metodi di registrazione di eliminare molta ridondanza.
supercat

5

L'articolo MSDN elenca il routing MVC (l'esempio che ha davvero fatto clic sul concetto per me) tra molti altri. Il paragrafo della descrizione (formattato) recita:

  • Quando si segnalano errori nel codice,
  • collegamento di collegamenti modello-view-controller (MVC),
  • eventi di modifica della proprietà modificata, ecc.,

spesso si desidera acquisire il nome della stringa di un metodo . L'uso di nameof aiuta a mantenere il codice valido durante la ridenominazione delle definizioni.

Prima di utilizzare i valori letterali delle stringhe per fare riferimento alle definizioni, che è fragile quando si rinomina gli elementi del codice perché gli strumenti non sono in grado di controllare questi valori letterali delle stringhe.

Le risposte accettate / più votate forniscono già numerosi esempi concreti eccellenti.


3

Lo scopo del nameof dell'operatore è fornire il nome di origine degli artefatti.

Di solito il nome della fonte ha lo stesso nome del nome dei metadati:

public void M(string p)
{
    if (p == null)
    {
        throw new ArgumentNullException(nameof(p));
    }
    ...
}

public int P
{
    get
    {
        return p;
    }
    set
    {
        p = value;
        NotifyPropertyChanged(nameof(P));
    }
}

Ma questo potrebbe non essere sempre il caso:

using i = System.Int32;
...
Console.WriteLine(nameof(i)); // prints "i"

O:

public static string Extension<T>(this T t)
{
    return nameof(T); returns "T"
}

Un uso che ho dato è per la denominazione delle risorse:

[Display(
    ResourceType = typeof(Resources),
    Name = nameof(Resources.Title_Name),
    ShortName = nameof(Resources.Title_ShortName),
    Description = nameof(Resources.Title_Description),
    Prompt = nameof(Resources.Title_Prompt))]

Il fatto è che, in questo caso, non avevo nemmeno bisogno delle proprietà generate per accedere alle risorse, ma ora ho un tempo di compilazione per verificare l'esistenza delle risorse.


0

Uno degli usi della nameofparola chiave è per l'impostazione Bindingin WPF programmazione .

per impostare Bindingdevi impostare Pathcon stringa e connameof parola chiave, è possibile utilizzare l'opzione Refactor.

Ad esempio, se si dispone della IsEnableproprietà di dipendenza nella propria UserControle si desidera associarla a IsEnableuna parte CheckBoxdella propria UserControl, è possibile utilizzare questi due codici:

CheckBox chk = new CheckBox();
Binding bnd = new Binding ("IsEnable") { Source = this };
chk.SetBinding(IsEnabledProperty, bnd);

e

CheckBox chk = new CheckBox();
Binding bnd = new Binding (nameof (IsEnable)) { Source = this };
chk.SetBinding(IsEnabledProperty, bnd);

È ovvio che il primo codice non può refactoring ma quello secend ...


0

In precedenza usavamo qualcosa del genere:

// Some form.
SetFieldReadOnly( () => Entity.UserName );
...
// Base form.
private void SetFieldReadOnly(Expression<Func<object>> property)
{
    var propName = GetPropNameFromExpr(property);
    SetFieldsReadOnly(propName);
}

private void SetFieldReadOnly(string propertyName)
{
    ...
}

Motivo: compilare il tempo di sicurezza. Nessuno può rinominare silenziosamente la proprietà e rompere la logica del codice. Ora possiamo usare nameof ().


0

Ha un vantaggio quando si utilizza ASP.Net MVC. Quando usi l'helper HTML per creare un controllo in vista, usa i nomi delle proprietà nell'attribuzione del nome dell'input html:

@Html.TextBoxFor(m => m.CanBeRenamed)

Fa qualcosa del genere:

<input type="text" name="CanBeRenamed" />

Quindi ora, se hai bisogno di convalidare la tua proprietà nel metodo Convalida puoi farlo:

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
  if (IsNotValid(CanBeRenamed)) {
    yield return new ValidationResult(
      $"Property {nameof(CanBeRenamed)} is not valid",
      new [] { $"{nameof(CanBeRenamed)}" })
  }
}

Nel caso in cui rinominassi la tua proprietà utilizzando gli strumenti di refactoring, la tua validazione non verrà interrotta.


0

Un altro caso d'uso di nameofè controllare le pagine delle schede, invece di controllare l'indice è possibile controllare la Nameproprietà delle pagine come segue:

if(tabControl.SelectedTab.Name == nameof(tabSettings))
{
    // Do something
}

Meno disordinato :)


0

Trovo che nameofaumenti la leggibilità di istruzioni SQL molto lunghe e complesse nelle mie applicazioni. Fa emergere le variabili da quel mare di stringhe ed elimina il tuo compito di capire dove vengono utilizzate le variabili nelle tue istruzioni SQL.

public bool IsFooAFoo(string foo, string bar)
{
    var aVeryLongAndComplexQuery = $@"SELECT yada, yada
    -- long query in here
    WHERE fooColumn = @{nameof(foo)}
    AND barColumn = @{nameof(bar)}
    -- long query here";


    SqlParameter[] parameters = {
        new SqlParameter(nameof(foo), SqlDBType.VarChar, 10){ Value = foo },
        new SqlParameter(nameof(bar), SqlDBType.VarChar, 10){ Value = bar },
    }
}
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.