Jaki jest cel nameof?


263

Wersja 6.0 ma nową funkcję nameof, ale nie rozumiem jej celu, ponieważ po prostu pobiera nazwę zmiennej i zmienia ją na ciąg znaków podczas kompilacji.

Myślałem, że może mieć jakiś cel podczas używania, <T>ale kiedy próbuję, po nameof(T)prostu drukuje mi Tzamiast używanego typu.

Masz pomysł na cel?



28
Nie było sposobu, aby to Twcześniej uzyskać . Był sposób na uzyskanie używanego typu wcześniej.
Jon Hanna

Początkowo wydawało się to przesadą i wciąż nie widzę przekonującego powodu, aby z niego korzystać. Może przykład MVC?
Corey Alix,

15
Zdecydowanie przydatny, gdy zmienna / zmiana nazwy w nazwie nameof. Pomaga także zapobiegać literówkom.
bvj

4
Oficjalna dokumentacja nameof została przeniesiona tutaj: docs.microsoft.com/en-us/dotnet/csharp/language-reference/… - Wymienia również kluczowe przypadki użycia, które stanowią całkiem dobrą odpowiedź na pytanie.
markus s

Odpowiedzi:


322

Co z przypadkami, w których chcesz ponownie użyć nazwy właściwości, na przykład podczas zgłaszania wyjątku na podstawie nazwy właściwości lub obsługi PropertyChangedzdarzenia. Istnieje wiele przypadków, w których chciałbyś mieć nazwę nieruchomości.

Weź ten przykład:

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

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

W pierwszym przypadku zmiana nazwy SomeProperty zmieni również nazwę właściwości lub przerwie kompilację. Ostatni przypadek nie.

Jest to bardzo przydatny sposób na utrzymanie kompilacji kodu i brak błędów (sortowanie).

( Bardzo fajny artykuł Erica Lipperta, dlaczego tego infoofnie zrobił, a nameofzrobił)


1
Rozumiem o tym, dodając, że resharper zmienia ciągi znaków podczas refaktoryzacji nazw, nie jestem pewien, czy VS ma podobną funkcjonalność.
Ash Burlaczenko

7
To ma. Ale zarówno Resharper, jak i VS nie działają na przykład nad projektami. To robi. W rzeczywistości jest to lepsze rozwiązanie.
Patrick Hofman

49
Innym częstym przypadkiem użycia jest routing w MVC using nameofi nazwa akcji zamiast łańcucha na stałe.
RJ Cuthbertson

2
@sotn Nie jestem pewien, czy rozumiem, o co pytasz. Nic nie stoi na przeszkodzie, abyś używał go w podobny sposób public class MyController { public ActionResult Index() { return View(nameof(Index)); } }- i możesz go używać nameofna elementach niestatycznych (na przykład możesz zadzwonić, nameof(MyController.Index)używając klasy powyżej i wyemituje on „Indeks”). Sprawdź przykłady na msdn.microsoft.com/en-us/library/…
RJ Cuthbertson

2
Nie rozumiem, dlaczego to jest wyjątkowe. Zmienne nazwy są zawsze takie same, prawda? Niezależnie od tego, czy masz instancję, czy nie, nazwy zmiennych nie zmienią się @sotn
Patrick Hofman,

176

Jest naprawdę przydatny ArgumentExceptioni jego pochodne:

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

Teraz, jeśli ktoś inputzmieni nazwę parametru, wyjątek również będzie aktualizowany.

Jest także przydatny w niektórych miejscach, w których wcześniej trzeba było zastosować odbicie, aby uzyskać nazwy właściwości lub parametrów.

W twoim przykładzie nameof(T)dostaje nazwę parametru typu - może to być również przydatne:

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

Innym zastosowaniem nameofjest wyliczanie - zwykle jeśli chcesz, aby nazwa ciągu wyliczenia była używana .ToString():

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

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

> "FooBar"

Jest to faktycznie stosunkowo wolne, ponieważ .Net przechowuje wartość wyliczania (tj. 7) I znajduje nazwę w czasie wykonywania.

Zamiast tego użyj nameof:

Console.WriteLine(nameof(MyEnum.FooBar))

> "FooBar"

Teraz .Net zastępuje nazwę wyliczenia ciągiem w czasie kompilacji.


Jeszcze inne zastosowanie to takie jak INotifyPropertyChangedlogowanie i logowanie - w obu przypadkach chcesz, aby nazwa członka, do którego dzwonisz, została przekazana do innej metody:

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

Lub...

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

9
I umieściłeś kolejną fajną funkcję: interpolację ciągów!
Patrick Hofman

1
@PatrickHofman i typeof(T), który jest kolejnym kawałkiem cukru w ​​czasie kompilacji, który jest przydatny w podobnych okolicznościach :-)
Keith

1
Brakuje mi tylko jednej rzeczy nameofthismethod. Możesz użyć, Log.Error($"Error in {nameof(DoSomething)}...")ale jeśli skopiujesz i wkleisz to do innych metod, nie zauważysz, że nadal się do nich odnosi DoSomething. Tak więc, chociaż działa idealnie z lokalnymi zmiennymi lub parametrami, nazwa metody stanowi problem.
Tim Schmelter

3
Czy wiesz, czy nameOfużyje [DisplayName]atrybutu, jeśli jest obecny? Na enumprzykład [DisplayName]często używam z projektami MVC
Luke T O'Brien

2
@AaronLS Tak, jest dość wyspecjalizowany i nie jest czymś, z czego często byś korzystał. Jednak, że throw newjest zupełnie inny anty-wzorzec - znajdę nad wykorzystujących catchbyć częstym problemem z mniejszych deweloperów, ponieważ czuje się jak wyeliminować problem (gdy przez większość czasu to tylko ukrywanie go).
Keith

26

Kolejny przypadek użycia, w którym nameofprzydatna jest funkcja C # 6.0 - Rozważmy bibliotekę taką jak Dapper, która znacznie ułatwia pobieranie DB. Chociaż jest to świetna biblioteka, musisz wpisać twarde nazwy właściwości / pól w zapytaniu. Oznacza to, że jeśli zdecydujesz się zmienić nazwę swojej nieruchomości / pola, istnieje duże prawdopodobieństwo, że zapomnisz zaktualizować zapytanie w celu użycia nowych nazw pól. Dzięki interpolacji ciągów i nameoffunkcjom kod staje się znacznie łatwiejszy w utrzymaniu i bezpieczny w użyciu.

Z przykładu podanego w linku

bez nazwy

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

o nazwie

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

3
Uwielbiam Dappera i bardzo lubię miedzy sobą ciągi znaków, ale IMO to wygląda tak brzydko. Ryzyko zerwania zapytania poprzez zmianę nazwy kolumny wydaje się tak małe w porównaniu z tak brzydkimi zapytaniami. Na pierwszy rzut oka powiedziałbym, że wolę pisać zapytania EF LINQ lub stosować się do konwencji takiej jak [TableName]. [ColumnName], w której mogę łatwo znaleźć / zamienić moje zapytania, kiedy zajdzie taka potrzeba.
drizin

@drizin Używam Dapper FluentMap, aby zapobiec takim zapytaniom (a także w celu rozdzielenia obaw)
mamuesstack

21

Twoje pytanie wyraża już cel. Musisz zobaczyć, że może to być przydatne do rejestrowania lub zgłaszania wyjątków.

na przykład.

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

to dobrze, jeśli zmienię nazwę zmiennej, kod zostanie uszkodzony lub zwróci wyjątek z niepoprawnym komunikatem .


Oczywiście zastosowania nie ograniczają się do tej prostej sytuacji. Możesz użyć, nameofilekroć przydatne byłoby zakodowanie nazwy zmiennej lub właściwości.

Zastosowania są różnorodne, gdy weźmie się pod uwagę różne sytuacje wiązania i refleksji. Jest to doskonały sposób na skompilowanie błędów, które były błędami w czasie wykonywania.


6
@atikot: Ale jeśli zmienisz nazwę zmiennej, kompilator nie zauważy, że łańcuch już nie pasuje.
LUB Mapper

1
Właściwie używam resharpera, który bierze to pod uwagę, ale rozumiem twój punkt widzenia.
atikot

4
@atikot, ja też, ale Resharper generuje tylko ostrzeżenie, a nie błąd kompilatora. Jest różnica między pewnością a dobrą radą.
Jodrell

1
@atikot, a Resharper nie sprawdza rejestrowania wiadomości
Jodrell

2
@Jodrell: I podejrzewam, że nie sprawdza też różnych innych zastosowań - co powiesz na powiązania WPF utworzone za pomocą kodu, niestandardowych OnPropertyChangedmetod (które bezpośrednio akceptują nazwy właściwości zamiast PropertyChangedEventArgs), lub wezwania do refleksji w celu wyszukania konkretnego członek czy typ?
LUB Mapper

13

Najczęstszym przypadkiem użycia, jaki mogę wymyślić, jest praca z INotifyPropertyChangedinterfejsem. (Zasadniczo wszystko związane z WPF i powiązaniami korzysta z tego interfejsu)

Spójrz na ten przykład:

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

Jak widać po staremu, musimy przekazać ciąg znaków wskazujący, która właściwość uległa zmianie. Dzięki nameofmożemy użyć nazwy nieruchomości bezpośrednio. To nie może wydawać się wielkim problemem. Ale wyobraź sobie, co się dzieje, gdy ktoś zmienia nazwę nieruchomości Foo. Gdy użyjesz łańcucha, wiązanie przestanie działać, ale kompilator cię nie ostrzeże. Podczas używania nameof pojawia się błąd kompilatora, że ​​nie ma właściwości / argumentu o nazwie Foo.

Zauważ, że niektóre frameworki używają magii odbicia, aby uzyskać nazwę właściwości, ale teraz mamy nazwę tego nie jest już konieczne .


5
Chociaż jest to prawidłowe podejście, wygodniejszym (i SUCHYM) podejściem jest użycie [CallerMemberName]atrybutu parametru nowej metody do wywołania tego zdarzenia.
Drew Noakes

1
Zgadzam się, CallerMemberName jest również miły, ale jest to osobny przypadek użycia, ponieważ (jak powiedziałeś) możesz go używać tylko w metodach. Jeśli chodzi o DRY, nie jestem pewien, czy [CallerMemberName]string x = nulljest lepszy niż nameof(Property). Można powiedzieć, że nazwa właściwości jest używana dwukrotnie, ale w zasadzie jest to argument przekazany do funkcji. Chyba nie do końca to, co oznacza DRY :).
Roy T.

Właściwie możesz go użyć we właściwościach. Oni też są członkami. Zaletą nameofjest to, że narzędzie do ustawiania właściwości wcale nie musi określać nazwy właściwości, co eliminuje możliwość kopiowania / wklejania błędów.
Drew Noakes,

4
Jest to sytuacja INotifyPropertyChanged, w której lepiej jest razem , używając[CallerMemberNameAttribute] pozwala na czyste zgłoszenie zmiany z ustawiacza właściwości, a nameofskładnia pozwala na czyste zgłoszenie zmiany z innej lokalizacji w kodzie.
Andrew Hanlon

9

Najczęstszym zastosowaniem będzie sprawdzanie poprawności danych wejściowych, np

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

W pierwszym przypadku, jeśli byłaby sposób zmieniający PAR nazwę parametru, prawdopodobnie będziesz zapomnij zmienić w ArgumentNullException . Z imieniem nie musisz się tym martwić.

Zobacz także: nameof (C # i Visual Basic Reference)


7

Projekt ASP.NET Core MVC wykorzystuje nameofwAccountController.cs i ManageController.csze RedirectToActionsposobu odwołać akcję w kontrolerze.

Przykład:

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

To przekłada się na:

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

i przenosi użytkownika do akcji „Indeks” w kontrolerze „Dom”, tj /Home/Index.


Dlaczego nie pójść na całość i używać return RedirectToAction(nameof(HomeController.Index), nameof(HomeController).Substring(nameof(HomeController),0,nameof(HomeController).Length-"Controller".Length));?
Suncat2000

@ Suncat2000, ponieważ jedna z tych rzeczy jest wykonywana podczas kompilacji, a druga nie? :)
Dinerdo

6

Jak już zauważyli inni, nameof operator wstawia nazwę nadaną elementowi w kodzie źródłowym.

Chciałbym dodać, że jest to naprawdę dobry pomysł w zakresie refaktoryzacji, ponieważ sprawia, że ​​refaktoryzacja łańcucha jest bezpieczna. Wcześniej korzystałem z metody statycznej, która korzystała z odbicia w tym samym celu, ale ma to wpływ na wydajność środowiska wykonawczego. nameofOperator ma wpływ na działanie środowiska wykonawczego; wykonuje swoją pracę w czasie kompilacji. Jeśli spojrzysz na MSILkod, znajdziesz ciąg osadzony. Zobacz następującą metodę i jej zdemontowany kod.

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

Może to jednak stanowić wadę, jeśli planujesz zaciemnić oprogramowanie. Po zaciemnieniu osadzony ciąg może już nie pasować do nazwy elementu. Mechanizmy oparte na tym tekście się zepsują. Przykładami tego, w tym między innymi: Reflection, NotifyPropertyChanged ...

Określenie nazwy w czasie wykonywania kosztuje pewną wydajność, ale jest bezpieczne dla zaciemnienia. Jeśli zaciemnianie nie jest wymagane ani planowane, zaleciłbym użycie nameofoperatora.


5

Weź pod uwagę, że używasz zmiennej w kodzie i musisz uzyskać nazwę zmiennej, i powiedzmy, wydrukuj ją, powinieneś użyć

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

A jeśli ktoś zmieni kod i użyje innej nazwy dla „myVar”, będzie musiał obserwować wartość ciągu w twoim kodzie i odpowiednio ją chenge.

Zamiast tego, gdybyś miał

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

Pomoże to automatycznie refaktoryzować!


Chciałbym, aby istniała specjalna składnia parametru zmiennego, która przekaże tablicę krotek, po jednej dla każdego parametru, zawierającą reprezentację kodu źródłowego Type, wartość i. Dzięki temu kod wywołujący metody rejestrowania może wyeliminować wiele nadmiarowości.
supercat

5

Artykuł MSDN wymienia routing MVC (przykład, który naprawdę kliknął moją koncepcję) wśród kilku innych. (Sformatowany) akapit opisu brzmi:

  • Zgłaszając błędy w kodzie,
  • podłączanie linków model-view-controller (MVC),
  • uruchamianie zdarzeń zmieniających właściwość itp.,

często chcesz uchwycić nazwę ciągu metody . Użycie nameof pomaga zachować poprawność kodu podczas zmiany nazw definicji.

Wcześniej trzeba było używać literałów ciągowych w celu odniesienia się do definicji, co jest kruche przy zmianie nazw elementów kodu, ponieważ narzędzia nie wiedzą, jak je sprawdzić.

Akceptowane / najwyżej oceniane odpowiedzi podają już kilka doskonałych konkretnych przykładów.


3

Cel nameof operatora jest podanie nazwy źródła artefaktów.

Zwykle nazwa źródła jest taka sama jak nazwa metadanych:

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

Ale nie zawsze tak może być:

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

Lub:

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

Jednym z zastosowań, które mu nadałem, jest nazywanie zasobów:

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

Faktem jest, że w tym przypadku nawet nie potrzebowałem wygenerowanych właściwości, aby uzyskać dostęp do zasobów, ale teraz sprawdzam, czy zasoby istnieją.


0

Jednym ze sposobów użycia nameofsłowa kluczowego jest programowe ustawienie Bindingw wpf .

aby ustawić Bindingmusisz ustawić Pathciąg, a nameofsłowem kluczowym można użyć opcji Refaktoryzuj.

Na przykład, jeśli masz IsEnablewłaściwość zależności UserControli chcesz ją powiązać IsEnablez niektórymi CheckBoxz ciebie UserControl, możesz użyć tych dwóch kodów:

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

i

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

To oczywiste, że pierwszy kod nie może zostać zmieniony, ale ten zabezpieczający ...


0

Wcześniej używaliśmy czegoś takiego:

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

Powód - skompiluj bezpieczeństwo czasu. Nikt nie może po cichu zmienić nazwy właściwości i złamać logiki kodu. Teraz możemy użyć nameof ().


0

Ma to zaletę, gdy używasz ASP.Net MVC. Kiedy używasz pomocnika HTML do zbudowania kontroli, używa nazw właściwości w atrybucie nazwy danych wejściowych HTML:

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

To sprawia, że ​​coś takiego:

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

Jeśli teraz chcesz zweryfikować swoją właściwość za pomocą metody Validate, możesz to zrobić:

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

W przypadku zmiany nazwy nieruchomości za pomocą narzędzi do refaktoryzacji weryfikacja nie zostanie naruszona.


0

Innym przykładem użycia nameofjest sprawdzenie stron kart, zamiast sprawdzania indeksu można sprawdzić Namewłaściwości stron kart w następujący sposób:

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

Mniej bałaganu :)


0

Uważam, że nameofzwiększa to czytelność bardzo długich i złożonych instrukcji SQL w moich aplikacjach. To sprawia, że ​​zmienne wyróżniają się z tego morza ciągów znaków i eliminuje zadanie polegające na ustaleniu, gdzie zmienne są używane w instrukcjach 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 },
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.