Den nameof-Operator

Tip

Dieser Artikel ist Teil des Abschnitts "Grundlagen" für Entwickler, die bereits mindestens eine Programmiersprache kennen und C# erlernen. Wenn Sie noch nicht mit der Programmierung vertraut sind, beginnen Sie zuerst mit den Lernprogrammen " Erste Schritte ". Die vollständige Operatorreferenz finden Sie unter nameof in der Sprachreferenz.

Kommen Sie aus einer anderen Sprache? Andere Sprachen weisen ähnliche Features auf. Javas reflektierendes Class.getSimpleName(), JavaScripts Function.name und Object.keys, Pythons __name__ und vars() und Swifts #function/#keyPath. Im Gegensatz zu den meisten davon ist das nameof von C# ein reines Konstrukt zur Kompilierzeit. Es verwendet keine Reflexion, weist zur Laufzeit nichts zu und erzeugt eine Konstante string , die in die Assembly integriert ist.

Der nameof Operator gibt den Textbezeichner eines Symbols zurück, z. B. eine Variable, einen Parameter, einen Typ, ein Element oder einen Namespace, als Kompilierungszeitkonstante string . Überall, wo Sie andernfalls einen Bezeichner als Zeichenfolge hartcodieren würden, verwenden Sie nameof: Der Compiler überprüft, ob das Symbol existiert, und Umbenennungsrefaktorierungen aktualisieren das Ergebnis automatisch.

Was nameof zurückgibt

nameof wertet den endgültigen Bezeichner in seinem Operanden aus. Sie wird zur Kompilierungszeit ausgeführt und verursacht keine Laufzeitkosten.

// nameof produces the textual identifier of a symbol at compile time.
Console.WriteLine(nameof(Customer));        // Customer
Console.WriteLine(nameof(Customer.Name));   // Name

var customer = new Customer("Ada");
Console.WriteLine(nameof(customer));        // customer
Console.WriteLine(nameof(customer.Name));   // Name

Der Operand kann auch ein qualifizierter Ausdruck sein, bei dem mit dem Punktoperator von einem umschließenden Gültigkeitsbereich zu einem Member navigiert wird, beispielsweise customer.Name, System.Console oder List<int>.Enumerator. In diesem Fall wird nur der letzte Bezeichner erfasst: nameof(customer.Name) gibt "Name"zurück , nicht "customer.Name".

Argumentüberprüfung

Die klassische Verwendung erzeugt den Parameternamen in einer ausgelösten Ausnahme. Übergeben Sie nameof(parameter) statt der Literalzeichenfolge "parameter", damit die Meldung bei einer späteren Umbenennung nicht veraltet zurückbleibt:

try
{
    Greet("");
}
catch (ArgumentException ex)
{
    // The exception's ParamName is the literal "name", produced by nameof at compile time.
    Console.WriteLine($"{ex.ParamName}: {ex.Message}");
}

static void Greet(string name)
{
    if (string.IsNullOrWhiteSpace(name))
    {
        throw new ArgumentException("Name must be non-empty.", nameof(name));
    }
    Console.WriteLine($"Hello, {name}!");
}

Insbesondere bei Nullprüfungen sollten Sie Ausnahmehilfen verwenden. Diese Hilfsmethoden, wie ThrowIfNull, erfassen den Namen des Arguments automatisch über CallerArgumentExpressionAttribute, sodass kein separates nameof benötigt wird:

// ArgumentNullException.ThrowIfNull captures the argument's name automatically
// through [CallerArgumentExpression], so a separate nameof isn't required for
// the null check. Use nameof for cases the helpers don't cover.
Customer? maybeCustomer = null;

try
{
    Save(maybeCustomer);
}
catch (ArgumentNullException ex)
{
    Console.WriteLine(ex.ParamName);   // customer
}

static void Save(Customer? customer)
{
    ArgumentNullException.ThrowIfNull(customer);
    // ...
}

Verwenden Sie nameof für die Fälle, die von den Hilfsmethoden nicht abgedeckt werden: ArgumentException, ArgumentOutOfRangeException (wenn etwas anderes als ein einzelnes Argument validiert wird) und andere Guard-Meldungen.

Benachrichtigungen über Eigenschaftsänderungen

Typen, die INotifyPropertyChanged implementieren, lösen ein Ereignis aus, dessen Nutzdaten den Namen der geänderten Eigenschaft enthalten. Das Hartkodieren des Namens als Zeichenfolge verursacht einen stillen Fehler, wenn die Eigenschaft umbenannt wird, die Zeichenfolge jedoch nicht. Verwenden Sie nameof stattdessen:

public sealed class Person : INotifyPropertyChanged
{
    private string _name = "";

    public string Name
    {
        get => _name;
        set
        {
            if (_name == value) return;
            _name = value;
            // nameof keeps the property name and the change notification in sync.
            // Renaming the property automatically updates this argument.
            OnPropertyChanged(nameof(Name));
        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    private void OnPropertyChanged([CallerMemberName] string? propertyName = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Der Setter ruft OnPropertyChanged(nameof(Name)) auf, damit der Name der Eigenschaft und die Änderungsbenachrichtigung synchron bleiben. Führen Sie das Beispiel aus, um zu sehen, wie die Ereignisse ausgelöst werden:

var person = new Person();
person.PropertyChanged += (_, e) => Console.WriteLine($"changed: {e.PropertyName}");

person.Name = "Ada";    // changed: Name
person.Name = "Grace";  // changed: Name

nameof in Attributargumenten

nameof ist innerhalb von Attributargumenten gültig. Der Compiler löst Bezeichner im umgebenden Bereich auf, einschließlich der Parameter der Methode, auf die das Attribut abzielt. Dies ist die idiomatische Methode zum Verweisen auf einen Parameter aus einem Attribut wie NotNullIfNotNullAttribute:

// nameof works inside attribute arguments. The compiler resolves the
// identifier even when the attribute targets a method or its parameters.
Console.WriteLine(NormalizeOrNull("  hi  ") ?? "<null>");   // hi
Console.WriteLine(NormalizeOrNull(null) ?? "<null>");        // <null>

[return: NotNullIfNotNull(nameof(input))]
static string? NormalizeOrNull(string? input) => input?.Trim();

Wenn der Parameter umbenannt wird, wird das nameof Argument durch dieselbe Umgestaltung aktualisiert – das Attribut kann nicht veraltet sein.

Qualifizierte Namen

Gibt für jeden qualifizierten Ausdruck nameof nur den letzten Bezeichner zurück:

// For a qualified expression, nameof returns only the final identifier.
Console.WriteLine(nameof(System.Collections.Generic.List<int>)); // List
Console.WriteLine(nameof(Customer.Name));                        // Name

Wenn Sie den vollqualifizierten Namen benötigen, verwenden Sie Type.FullName für eine Instanz von Type. nameof ist für Bezeichner und keine Pfade bestimmt.

nameof Bezeichnerzeichenfolgen vorziehen

Überall, wo Sie im Code auf eine Methode, Eigenschaft, einen Parameter, Typ oder Namespace mit Namen verweisen, verwenden Sie nameof anstelle eines Zeichenfolgenliterals. Im Vergleich zu einer hartcodierten Zeichenfolge:

  • Der Compiler überprüft, ob das Symbol vorhanden ist. Ein Tippfehler wird zu einem Build-Fehler und nicht zu einem stillen Laufzeit-Bug.
  • Umbenennungs-Refactorings aktualisieren das Ergebnis automatisch. Hartcodierte Zeichenfolgen driften aus der Synchronisierung heraus.
  • Das Ergebnis ist eine Kompilierungszeitkonstante, sodass keine Laufzeitkosten anfallen.

Diese Empfehlung gilt für die Protokollierung von Nachrichten, Ausnahmeargumenten, Attributargumenten, Eigenschaftsänderungsbenachrichtigungen und Serialisierungsschlüsselkonstanten, die an einen Membernamen gebunden sind.

Siehe auch