A nameof operátor

Jótanács

Ez a cikk az Alapismeretek szakasz része azon fejlesztők számára, akik már legalább egy programozási nyelvet ismernek, és c#-t tanulnak. Ha még csak most ismerkedik a programozással, először az Első lépések oktatóanyagokkal kezdje. A teljes operátor-referenciát a nyelvi referenciában talál nameof .

Más nyelvről jön? Más nyelvek hasonló funkciókkal rendelkeznek. Java tükröző Class.getSimpleName(), a JavaScript Function.name és Object.keys, Python __name__ és vars(), valamint Swift #function/#keyPath. A legtöbbjükkel ellentétben a C# nameof-ja egy tisztán fordítási időben létező konstrukció. Nem használ reflexiót, futási idő alatt nem foglal le semmit, és egy olyan string konstanst hoz létre, amely be van építve az összeállításba.

Az nameof operátor egy szimbólum szöveges azonosítóját adja vissza, például változót, paramétert, típust, tagot vagy névteret fordítási időállandóként string . Mindenhol, ahol egyébként egy azonosítót karakterláncként adna meg, használja helyette a nameof elemet: a fordító ellenőrzi, hogy a szimbólum létezik-e, és az átnevezési refaktorálások automatikusan frissítik az eredményt.

nameof visszatérési értéke

nameof kiértékelése az operandus végleges azonosítóját adja eredményül. Fordításkor fut, és nem jár futásidejű többletköltséggel.

// 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

Az operandus lehet egy minősített kifejezés is, vagyis olyan kifejezés, amely a pontoperátort használja egy befoglaló hatókörből egy tag elérésére, például customer.Name, System.Console vagy List<int>.Enumerator. Ebben az esetben a rendszer csak az utolsó azonosítót rögzíti: nameof(customer.Name) visszaadja "Name", nem "customer.Name".

Argumentum érvényesítése

A klasszikus használat a paraméter nevét egy kidobott kivételben adja meg. Adjon át inkább nameof(parameter) a sztringliterálként megadott "parameter" helyett, hogy egy későbbi átnevezés ne tehesse félrevezetővé az üzenetet:

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

A kifejezetten null értékű ellenőrzéseknél inkább kivételsegítőket használ. Ezek a segítők, például a ThrowIfNull, automatikusan a CallerArgumentExpressionAttribute segítségével rögzítik az argumentum nevét, így nincs szükség külön nameof-re:

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

Használja a(z) nameof elemet azokban az esetekben, amelyeket a segédfüggvények nem fednek le: ArgumentException, ArgumentOutOfRangeException (ha nem egyetlen argumentumot ellenőriz), valamint egyéb ellenőrző üzenetek esetén.

Tulajdonságmódosítási értesítések

Azok a típusok, amelyek megvalósítják a INotifyPropertyChanged elemet, olyan eseményt váltanak ki, amelynek adattartalma tartalmazza a módosított tulajdonság nevét. A név sztringként történő hardkódolása észrevétlen hibához vezethet, ha a tulajdonságot átnevezik, de a sztringet nem. Használja inkább a következőt nameof :

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

A beállító meghívja a OnPropertyChanged(nameof(Name))-t, hogy a tulajdonságnév és a változásértesítés szinkronban maradjon. Futtassa a példát, hogy lássa, hogyan váltódnak ki az események:

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

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

nameof attribútumargumentumokban

nameof attribútumargumentumokon belül érvényes. A fordító feloldja a környező hatókörben található azonosítókat, beleértve annak a metódusnak a paramétereit is, amelyre az attribútum vonatkozik. Ez az idiomatikus módja annak, hogy egy paraméterre hivatkozzon egy attribútumból, például 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();

Ha a paramétert átnevezik, a nameof argumentum is frissül ugyanennek a refaktorálásnak a során – így az attribútum nem avulhat el.

Minősített nevek

Bármely minősített kifejezés nameof esetében csak az utolsó azonosítót adja vissza:

// 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

Ha a teljesen minősített névre van szüksége, használja a(z) Type.FullName elemet egy Type példányon. nameof azonosítókra, nem elérési utakra.

Azonosítóláncok helyett használjon nameof

Bárhol, ahol egy metódusra, tulajdonságra, paraméterre, típusra vagy névtérre név szerint hivatkozik a kódban, sztringliterál helyett a(z) nameof elemet használja. Egy kódba írt karakterlánchoz képest:

  • A fordító ellenőrzi, hogy létezik-e a szimbólum. Az elírás buildelési hibává válik, nem pedig csendes futtatókörnyezeti hibává.
  • Az átnevezési refaktorálások automatikusan frissítik az eredményt. A keménykódolt sztringek idővel nincsenek szinkronban.
  • Az eredmény egy fordítási időállandó, így nincs futásidejű költség.

Ez a javaslat a tagok nevéhez kötődő naplózási üzenetekre, kivételargumentumokra, attribútumargumentumokra, tulajdonságmódosítási értesítésekre és szerializálási kulcsállandókra vonatkozik.

Lásd még