Was ist der Zweck von nameof?


263

Version 6.0 hat eine neue Funktion von nameof, aber ich kann den Zweck nicht verstehen, da nur der Variablenname verwendet und beim Kompilieren in eine Zeichenfolge geändert wird.

Ich dachte, es könnte einen Zweck haben <T>, wenn ich nameof(T)es benutze, aber wenn ich es versuche, druckt es mir einfach ein Tanstelle des verwendeten Typs.

Irgendeine Idee zu diesem Zweck?



28
Es gab Tvorher keinen Weg, das zu bekommen . Es gab eine Möglichkeit, den verwendeten Typ vorher zu erhalten.
Jon Hanna

Anfangs schien es übertrieben und ich sehe immer noch keinen zwingenden Grund, es zu benutzen. Vielleicht ein MVC-Beispiel?
Corey Alix

15
Auf jeden Fall nützlich, wenn Sie den Namen in refactory / umbenennen nameof. Hilft auch, Tippfehler zu vermeiden.
bvj

4
Die offizielle Dokumentation von nameof wurde hierher verschoben : docs.microsoft.com/en-us/dotnet/csharp/language-reference/… - Außerdem werden wichtige Anwendungsfälle aufgelistet, die als ziemlich gute Antwort auf die Frage dienen.
Markus s

Antworten:


322

Was ist mit Fällen, in denen Sie den Namen einer Eigenschaft wiederverwenden möchten, z. B. wenn Sie eine Ausnahme basierend auf einem Eigenschaftsnamen auslösen oder ein PropertyChangedEreignis behandeln. Es gibt zahlreiche Fälle, in denen Sie den Namen der Immobilie haben möchten.

Nehmen Sie dieses Beispiel:

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

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

Im ersten Fall SomePropertyändert das Umbenennen auch den Namen der Eigenschaft, oder die Kompilierung wird unterbrochen. Der letzte Fall nicht.

Dies ist eine sehr nützliche Methode, um Ihren Code kompiliert und fehlerfrei zu halten.

(Ein sehr schöner Artikel von Eric Lippert, warum er infoofes nicht geschafft hat, während nameof)


1
Ich verstehe den Punkt, indem ich nur hinzufüge, dass Resharper die Zeichenfolgen beim Refactoring von Namen ändert, nicht sicher, ob VS ähnliche Funktionen hat.
Ash Burlaczenko

7
Es hat. Aber sowohl Resharper als auch VS arbeiten beispielsweise nicht an Projekten. Das macht. In der Tat ist dies die bessere Lösung.
Patrick Hofman

49
Ein weiterer häufiger Anwendungsfall ist das Routing in MVC mit nameofund der Name der Aktion anstelle einer fest codierten Zeichenfolge.
RJ Cuthbertson

2
@sotn Ich bin nicht sicher, ob ich verstehe, was Sie fragen. Nichts hindert Sie daran, es so zu verwenden public class MyController { public ActionResult Index() { return View(nameof(Index)); } }- und Sie können es nameoffür nicht statische Mitglieder verwenden (zum Beispiel können Sie nameof(MyController.Index)mit der obigen Klasse aufrufen und es wird "Index" ausgegeben). Schauen Sie sich die Beispiele unter msdn.microsoft.com/en-us/library/…
RJ Cuthbertson

2
Ich verstehe nicht, warum das so besonders ist. Variablennamen sind immer gleich, oder? Unabhängig davon, ob Sie eine Instanz haben oder nicht, ändern sich die Variablennamen nicht @sotn
Patrick Hofman

176

Es ist wirklich nützlich für ArgumentExceptionund seine Derivate:

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

Wenn nun jemand den Namen des inputParameters umgestaltet, wird auch die Ausnahme auf dem neuesten Stand gehalten.

Es ist auch an einigen Stellen nützlich, an denen zuvor Reflexionen verwendet werden mussten, um die Namen von Eigenschaften oder Parametern zu erhalten.

In Ihrem Beispiel nameof(T)wird der Name des Typparameters abgerufen - dies kann auch nützlich sein:

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

Eine andere Verwendung von nameofist für Aufzählungen - normalerweise, wenn Sie den Zeichenfolgennamen einer Aufzählung möchten, die Sie verwenden .ToString():

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

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

> "FooBar"

Dies ist tatsächlich relativ langsam, da .Net den Aufzählungswert (dh 7) enthält und den Namen zur Laufzeit findet.

Verwenden Sie stattdessen nameof:

Console.WriteLine(nameof(MyEnum.FooBar))

> "FooBar"

Jetzt ersetzt .Net den Namen der Aufzählung zur Kompilierungszeit durch eine Zeichenfolge.


Eine weitere Verwendung ist das INotifyPropertyChangedProtokollieren. In beiden Fällen möchten Sie, dass der Name des aufgerufenen Mitglieds an eine andere Methode übergeben wird:

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

Oder...

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

9
Und Sie haben eine weitere coole Funktion hinzugefügt: String-Interpolation!
Patrick Hofman

1
@PatrickHofman und typeof(T), das ist ein weiteres Stück Zucker zur Kompilierungszeit, das unter ähnlichen Umständen nützlich ist :-)
Keith

1
Eine Sache, die mir fehlt, ist so etwas wie nameofthismethod. Sie können verwenden, Log.Error($"Error in {nameof(DoSomething)}...")aber wenn Sie dies kopieren und in andere Methoden einfügen, werden Sie nicht bemerken, dass es sich immer noch darauf bezieht DoSomething. Während es perfekt mit lokalen Variablen oder Parametern funktioniert, ist der Methodenname ein Problem.
Tim Schmelter

3
Wissen Sie, ob nameOfdas [DisplayName]Attribut verwendet wird, falls vorhanden? Für das enumBeispiel, das ich [DisplayName]oft mit MVC-Projekten verwende
Luke T O'Brien

2
@AaronLS Ja, es ist ziemlich spezialisiert und nicht etwas, das Sie oft verwenden würden. Dies throw newist jedoch ein ganz anderes Anti-Muster - ich finde catch, dass Überbeanspruchung ein häufiges Problem bei Junior-Entwicklern ist, weil es sich anfühlt, als würde man das Problem beheben (wenn es die meiste Zeit nur versteckt ist).
Keith

26

Ein weiterer Anwendungsfall, bei dem die nameofFunktion von C # 6.0 nützlich ist - Stellen Sie sich eine Bibliothek wie Dapper vor, die das Abrufen von DBs erheblich vereinfacht. Obwohl dies eine großartige Bibliothek ist, müssen Sie die Eigenschafts- / Feldnamen in der Abfrage fest codieren. Dies bedeutet, dass bei einer Umbenennung Ihrer Eigenschaft / Ihres Feldes die Wahrscheinlichkeit groß ist, dass Sie vergessen, die Abfrage zu aktualisieren, um neue Feldnamen zu verwenden. Durch die Interpolation und nameofFunktionen von Zeichenfolgen wird Code viel einfacher zu warten und typsicher.

Aus dem Beispiel im Link

ohne nameof

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

mit nameof

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

3
Ich liebe Dapper und ich mag String-Interporlationen sehr, aber IMO sieht das so hässlich aus. Das Risiko, eine Abfrage durch Umbenennen einer Spalte zu beschädigen, scheint im Vergleich zu solch hässlichen Abfragen so gering zu sein. Auf den ersten Blick würde ich sagen, ich schreibe lieber EF LINQ-Abfragen oder folge einer Konvention wie [TableName]. [ColumnName], bei der ich meine Abfragen bei Bedarf leicht finden / ersetzen kann.
Drizin

@drizin Ich benutze Dapper FluentMap , um solche Abfragen zu verhindern (und auch um Bedenken zu trennen)
mamuesstack

21

Ihre Frage drückt bereits den Zweck aus. Sie müssen sehen, dass dies nützlich sein kann, um Ausnahmen zu protokollieren oder auszulösen.

beispielsweise.

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

Dies ist gut, wenn ich den Namen der Variablen ändere, wird der Code stattdessen unterbrochen oder es wird eine Ausnahme mit einer falschen Nachricht zurückgegeben .


Natürlich sind die Verwendungen nicht auf diese einfache Situation beschränkt. Sie können verwenden, nameofwann immer es nützlich wäre, den Namen einer Variablen oder Eigenschaft zu codieren.

Die Verwendungsmöglichkeiten sind vielfältig, wenn Sie verschiedene Bindungs- und Reflexionssituationen berücksichtigen. Es ist eine hervorragende Möglichkeit, Laufzeitfehler in die Kompilierungszeit einzubeziehen.


6
@atikot: Wenn Sie die Variable jedoch umbenennen, merkt der Compiler nicht, dass die Zeichenfolge nicht mehr übereinstimmt.
ODER Mapper

1
Ich benutze tatsächlich einen Resharper, der dies berücksichtigt, aber ich verstehe Ihren Standpunkt.
Atikot

4
@atikot, ich auch, aber Resharper generiert nur eine Warnung, keinen Compilerfehler. Es gibt einen Unterschied zwischen einer Gewissheit und einem guten Rat.
Jodrell

1
@atikot, und, Resharper überprüft keine Protokollierungsnachrichten
Jodrell

2
@Jodrell: Und ich vermute, es werden auch nicht verschiedene andere Verwendungszwecke überprüft - wie wäre es mit WPF-Bindungen, die in Code-Behind- OnPropertyChangedMethoden erstellt wurden (die eher Eigenschaftsnamen als akzeptieren PropertyChangedEventArgs), oder Aufrufe zur Reflexion, um nach einer bestimmten zu suchen Mitglied oder Typ?
ODER Mapper

13

Der häufigste Anwendungsfall, an den ich denken kann, ist die Arbeit mit der INotifyPropertyChangedSchnittstelle. (Grundsätzlich verwendet alles, was mit WPF und Bindungen zu tun hat, diese Schnittstelle.)

Schauen Sie sich dieses Beispiel an:

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)));           
        }
    }
}

Wie Sie auf die alte Weise sehen können, müssen wir eine Zeichenfolge übergeben, um anzugeben, welche Eigenschaft sich geändert hat. Mit können nameofwir den Namen der Immobilie direkt verwenden. Dies scheint keine große Sache zu sein. Aber stellen Sie sich vor, was passiert, wenn jemand den Namen der Immobilie ändert Foo. Bei Verwendung einer Zeichenfolge funktioniert die Bindung nicht mehr, der Compiler warnt Sie jedoch nicht. Wenn Sie nameof verwenden, erhalten Sie einen Compilerfehler, dass der Name keine Eigenschaft / kein Argument enthält Foo.

Beachten Sie, dass einige Frameworks Reflexionsmagie verwenden, um den Namen der Eigenschaft zu erhalten, aber jetzt haben wir den Namen, dass dies nicht mehr erforderlich ist .


5
Während dies ein gültiger Ansatz ist, besteht ein bequemerer (und trockener) Ansatz darin, das [CallerMemberName]Attribut für den Parameter einer neuen Methode zu verwenden, um dieses Ereignis auszulösen.
Drew Noakes

1
Ich bin damit einverstanden, dass CallerMemberName auch nett ist, aber es ist ein separater Anwendungsfall, da Sie es (wie Sie sagten) nur in Methoden verwenden können. Was DRY betrifft, bin ich mir nicht sicher, ob [CallerMemberName]string x = nulles besser ist als nameof(Property). Man könnte sagen, der Eigenschaftsname wird zweimal verwendet, aber das ist im Grunde das Argument, das an eine Funktion übergeben wird. Nicht wirklich was mit DRY gemeint ist denke ich :).
Roy T.

Eigentlich können Sie es in Eigenschaften verwenden. Sie sind auch Mitglieder. Der Vorteil gegenüber nameofist, dass der Eigenschaftssetzer den Eigenschaftsnamen überhaupt nicht angeben muss, wodurch die Möglichkeit von Fehlern beim Kopieren / Einfügen ausgeschlossen wird.
Drew Noakes

4
Es ist eine bessere Situation INotifyPropertyChanged, [CallerMemberNameAttribute]wenn Sie mithilfe der Option die Änderungsbenachrichtigung von einem Eigenschaftssetzer sauber auslösen können, während die nameofSyntax die saubere Benachrichtigung von einem anderen Ort in Ihrem Code aus ermöglicht.
Andrew Hanlon

9

Die häufigste Verwendung ist die Eingabevalidierung, z

//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));
}

Wenn Sie im ersten Fall die Methode umgestalten, die den Namen des Par- Parameters ändert , werden Sie wahrscheinlich vergessen, dies in der ArgumentNullException zu ändern . Mit nameof müssen Sie sich darüber keine Sorgen machen.

Siehe auch: nameof (C # und Visual Basic-Referenz)


7

Das ASP.NET Core MVC-Projekt verwendet nameofin AccountController.csund ManageController.csmit der RedirectToActionMethode, um auf eine Aktion im Controller zu verweisen.

Beispiel:

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

Dies bedeutet:

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

und führt den Benutzer zur Aktion 'Index' im 'Home'-Controller, d /Home/Index. h .


Warum nicht das ganze Schwein gehen und benutzen return RedirectToAction(nameof(HomeController.Index), nameof(HomeController).Substring(nameof(HomeController),0,nameof(HomeController).Length-"Controller".Length));?
Suncat2000

@ Suncat2000, weil eines dieser Dinge in der Kompilierung erledigt wird und das andere nicht? :)
Dinerdo

6

Wie andere bereits betont haben, nameoffügt der Operator den Namen ein, den das Element im Quellcode angegeben hat.

Ich möchte hinzufügen, dass dies eine wirklich gute Idee in Bezug auf das Refactoring ist, da es dieses String-Refactoring sicher macht. Zuvor habe ich eine statische Methode verwendet, bei der die Reflexion für denselben Zweck verwendet wurde, die sich jedoch auf die Laufzeitleistung auswirkt. Der nameofBediener hat keine Auswirkungen auf die Laufzeitleistung. Es erledigt seine Arbeit zur Kompilierungszeit. Wenn Sie sich den MSILCode ansehen , finden Sie die eingebettete Zeichenfolge. Siehe die folgende Methode und ihren zerlegten Code.

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

Dies kann jedoch ein Nachteil sein, wenn Sie Ihre Software verschleiern möchten. Nach der Verschleierung stimmt die eingebettete Zeichenfolge möglicherweise nicht mehr mit dem Namen des Elements überein. Mechanismen, die sich auf diesen Text stützen, brechen ab. Beispiele dafür, einschließlich, aber nicht beschränkt auf, sind: Reflection, NotifyPropertyChanged ...

Das Ermitteln des Namens zur Laufzeit kostet etwas Leistung, ist jedoch sicher zu verschleiern. Wenn eine Verschleierung weder erforderlich noch geplant ist, würde ich die Verwendung des nameofOperators empfehlen .


5

Bedenken Sie, dass Sie eine Variable in Ihrem Code verwenden und den Namen der Variablen abrufen müssen. Sagen wir, Sie müssen sie verwenden

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

Und wenn dann jemand den Code umgestaltet und einen anderen Namen für "myVar" verwendet, müsste er / sie auf den Zeichenfolgenwert in Ihrem Code achten und ihn entsprechend ändern.

Stattdessen, wenn Sie hatten

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

Es würde helfen, automatisch umzugestalten!


Ich wünschte, es hätte eine spezielle Variable-Parameter-Syntax gegeben, die ein Array von Tupeln übergeben würde, eines für jeden Parameter, das die Quellcodedarstellung, das Typeund den Wert enthält. Dies würde es Code ermöglichen, Protokollierungsmethoden aufzurufen, um viel Redundanz zu beseitigen.
Supercat

5

Der MSDN-Artikel listet unter anderem das MVC-Routing (das Beispiel, das für mich wirklich auf das Konzept geklickt hat) auf. Der (formatierte) Beschreibungsabsatz lautet:

  • Wenn Sie Fehler im Code melden,
  • Anschließen von MVC-Links (Model-View-Controller),
  • Brändeigenschaft veränderte Ereignisse usw.,

Sie möchten häufig den Zeichenfolgennamen einer Methode erfassen . Die Verwendung von nameof hilft dabei , Ihren Code beim Umbenennen von Definitionen gültig zu halten.

Bevor Sie Zeichenfolgenliterale verwenden mussten , um auf Definitionen zu verweisen, ist dies beim Umbenennen von Codeelementen spröde, da Tools diese Zeichenfolgenliterale nicht überprüfen können.

Die akzeptierten / am besten bewerteten Antworten geben bereits einige hervorragende konkrete Beispiele.


3

Der Zweck des nameofOperators besteht darin, den Quellennamen der Artefakte anzugeben.

Normalerweise ist der Quellenname derselbe Name wie der Metadatenname:

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));
    }
}

Dies ist jedoch möglicherweise nicht immer der Fall:

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

Oder:

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

Eine Verwendung, die ich ihm gegeben habe, ist das Benennen von Ressourcen:

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

Tatsache ist, dass ich in diesem Fall nicht einmal die generierten Eigenschaften für den Zugriff auf die Ressourcen benötigte, sondern jetzt eine Überprüfung der Kompilierungszeit habe, ob die Ressourcen vorhanden sind.


0

Ein nameofSchlüsselwort wird zum programmgesteuerten Einstellen Bindingvon wpf verwendet .

Zum Festlegen müssen BindingSie Pathmit Zeichenfolge und mit nameofSchlüsselwort festlegen. Es ist möglich, die Refactor-Option zu verwenden.

Wenn Sie beispielsweise eine IsEnableAbhängigkeitseigenschaft in Ihrer haben UserControlund diese an IsEnableeinige CheckBoxin Ihrer binden möchten UserControl, können Sie diese beiden Codes verwenden:

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

und

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

Es ist offensichtlich, dass der erste Code nicht umgestaltet werden kann, aber der zweite ...


0

Zuvor haben wir so etwas verwendet:

// 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)
{
    ...
}

Grund - Zeitsicherheit kompilieren. Niemand kann die Eigenschaft stillschweigend umbenennen und die Codelogik unterbrechen. Jetzt können wir nameof () verwenden.


0

Dies ist von Vorteil, wenn Sie ASP.Net MVC verwenden. Wenn Sie den HTML-Helfer verwenden, um ein Steuerelement in der Ansicht zu erstellen, werden Eigenschaftsnamen in der Namenszuordnung der HTML-Eingabe verwendet:

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

Es macht so etwas:

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

Wenn Sie nun Ihre Eigenschaft in der Validate-Methode validieren müssen, können Sie Folgendes tun:

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

Wenn Sie Ihre Eigenschaft mithilfe von Refactoring-Tools umbenennen, wird Ihre Validierung nicht unterbrochen.


0

Ein weiterer Anwendungsfall von nameofist das Überprüfen von Registerkarten. Anstatt den Index zu überprüfen, können Sie die NameEigenschaften der Registerkarten wie folgt überprüfen :

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

Weniger chaotisch :)


0

Ich finde, dass dies nameofdie Lesbarkeit sehr langer und komplexer SQL-Anweisungen in meinen Anwendungen erhöht. Dadurch heben sich die Variablen von diesem Meer von Zeichenfolgen ab und Sie müssen nicht mehr herausfinden, wo die Variablen in Ihren SQL-Anweisungen verwendet werden.

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 },
    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.