2015-07-29 3 views
171

Версия 6.0 получила новую функцию nameof, но я не могу понять ее цели, так как она просто принимает имя переменной и изменяет ее на строку в компиляции.Какова цель имени?

Я думал, что это может иметь определенную цель при использовании <T>, но когда я пытаюсь сделать nameof(T), он просто печатает меня T вместо используемого типа.

Любая идея с этой целью?

+1

Смотрите также https://msdn.microsoft.com /library/dn986596.aspx – Corak

+21

Не было способа получить этот 'T' раньше. Раньше был способ получить использованный тип. –

+0

Первоначально это казалось излишним, и я до сих пор не вижу веских причин использовать его. Может быть, пример MVC? –

ответ

208

Как насчет случаев, когда вы хотите повторно использовать имя свойства, например, при метании исключения на основе имени свойства или обработки события PropertyChanged. Существует множество случаев, когда вы хотели бы иметь имя свойства.

Возьмем такой пример:

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

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

В первом случае, переименование SomeProperty изменит имя свойства тоже, или он сломается компиляцию. В последнем случае нет.

Это очень полезный способ сохранить компиляцию кода и ошибку (сортировку).

(A very nice article from Eric Lippert почему infoof не сделал это, в то время как nameof сделал)

+0

Насколько я понимаю, просто добавив, что resharper изменяет строки при реорганизации имен, не уверен, что VS имеет аналогичную функциональность. –

+5

У него есть. Но как Resharper, так и VS не работают над проектами, например. Это так. На самом деле это лучшее решение. –

+23

Другим распространенным вариантом использования является маршрутизация в MVC с использованием 'nameof' и имени действия вместо стробированной строки. –

124

Это действительно полезно для ArgumentException и его производных:

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

Теперь, если кто-то refactors имя параметра input исключение будет храниться до даты тоже.

Он также полезен в некоторых местах, где ранее использовалось отражение, чтобы получить имена свойств или параметров.

В вашем примере nameof(T) получает имя параметра типа - это может быть полезно также:

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

Другое использование nameof для перечислений - как правило, если вы хотите, чтобы имя строковое перечисление вы используете .ToString():

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

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

> "FooBar" 

Это на самом деле довольно медленно, как .Net содержит значение перечисления (т.е. 7) и находит имя во время выполнения.

Вместо этого используйте nameof:

Console.WriteLine(nameof(MyEnum.FooBar)) 

> "FooBar" 

Теперь .Net заменяет имя перечисления со строкой во время компиляции.


Еще одно применение для таких вещей, как INotifyPropertyChanged и лесозаготовок - в обоих случаях вы хотите имя элемента, который вы звоните, чтобы быть передан другому методу:

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

Или .. .

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

И вы добавили еще одну интересную функцию в: строчную интерполяцию! –

+1

@PatrickHofman и 'typeof (T)', который является еще одним куском времени компиляции, который полезен в подобных обстоятельствах :-) – Keith

+0

Одна вещь, которую я пропускаю, - это нечто вроде 'nameofthismethod'. Вы можете использовать 'Log.Error ($" Ошибка в {nameof (DoSomething)} ... ")', но если вы скопируете это в другие методы, вы не заметите, что это все еще относится к 'DoSomething'. Поэтому, когда он отлично работает с локальными переменными или параметрами, имя метода является проблемой. –

16

Ваш вопрос уже выражает цель. Вы должны заметить, что это может быть полезно для регистрации или исключения исключений.

например.

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

это хорошо, если изменить имя переменной код сломается вместо или возвращать исключение с неправильным сообщением.


Конечно, использование не ограничивается этой простой ситуацией. Вы можете использовать nameof всякий раз, когда было бы полезно закодировать имя переменной или свойства.

Использование многообразно, если вы рассматриваете различные ситуации привязки и отражения. Это отличный способ донести до ошибок время компиляции.

+0

для регистрации я мог просто написать имя переменной самостоятельно, было бы короче – atikot

+5

@atikot: Но тогда, если вы переименуете переменную, компилятор не заметит, что строка больше не соответствует. –

+1

На самом деле я использую resharper, который берет его на учетную запись, но я вижу вашу точку. – atikot

8

Наиболее общее использование будет в проверке входных данных, таких как

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

В первом случае, если вы реорганизовать метод изменение имя параметра, вы, вероятно, забудете изменить это значение в ArgumentNullException. С nameof вам не о чем беспокоиться.

Смотрите также: nameof (C# and Visual Basic Reference)

5

Считают, что вы используете переменную в коде и нужно, чтобы получить имя переменной и позволяет сказать, что печатать, вы должны использовать

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

И если то кто-то реорганизует код и использует другое имя для «myVar», он/она должен будет следить за строковым значением в вашем коде и соответствующим образом указывать его.

Вместо если вы имели

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

Это поможет автоматически реорганизовать!

+0

Я бы хотел, чтобы был специальный синтаксис переменных-параметров, который передавал бы массив кортежей, по одному для каждого параметра, содержащему представление исходного кода, «Тип» и значение. Это позволит коду, вызывающему методы ведения журнала, устранить много избыточности. – supercat

10

Самый распространенный вариант использования, который я могу придумать, - это работать с интерфейсом INotifyPropertyChanged. (В основном все, что связано с WPF и привязки использует этот интерфейс)

Взгляните на этот пример:

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

Как вы можете видеть по-старому, мы должны передать строку, чтобы указать, какое свойство изменилось , С nameof мы можем напрямую использовать имя свойства. Это может показаться неважным. Но изображение, что происходит, когда кто-то меняет имя свойства Foo. При использовании строки привязка перестанет работать, но компилятор не предупредит вас. При использовании nameof вы получаете ошибку компилятора, что нет свойства/аргумента с именем Foo.

Обратите внимание, что некоторые фреймворки используют магию отражения, чтобы получить имя свойства, но теперь у нас есть nameof, это больше не обязательно.

+5

Несмотря на то, что это правильный подход, более удобный (и DRY) подход заключается в использовании атрибута '[CallerMemberName]' для параметра нового метода для повышения этого события. –

+1

Я согласен, что CallerMemberName тоже приятный, но его отдельный прецедент, поскольку (как вы сказали) вы можете использовать его только в методах. Что касается DRY, я не уверен, что строка '[CallerMemberName] x = null' лучше, чем' nameof (Property) '. Вы можете сказать, что имя свойства используется дважды, но это в основном аргумент, переданный функции. Не совсем то, что имеется в виду с DRY, я думаю :). –

+0

Фактически вы можете использовать его в свойствах. Они тоже члены. Преимущество над 'nameof' заключается в том, что средство настройки свойств не обязательно должно указывать имя свойства вообще, исключая возможность копирования/вставки ошибок. –

2

Целью оператора nameof является предоставление имени источника артефактов.

Обычно имя источника такое же имя, как имя метаданных:

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

Но это не всегда может быть:

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

Или:

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

One использование, которое я ему давал для присвоения имен:

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

Дело в том, что в этом случае мне даже не нужны сгенерированные свойства для доступа к ресурсам, но теперь у меня есть проверка времени компиляции, что ресурсы существуют.

6

Как уже указывали другие, оператор nameof вставляет имя, которое элемент дал в исходном коде.

Я хотел бы добавить, что это действительно хорошая идея с точки зрения рефакторинга, поскольку он делает эту рефакторингу этой строки безопасной. Раньше я использовал статический метод, который использовал отражение для той же цели, которая, однако, имеет влияние производительности во время выполнения. Оператор nameof не имеет влияния на производительность во время выполнения, он выполняет свою работу по времени компиляции. Если вы посмотрите на код MSIL, вы найдете встроенную строку. См. Следующий метод и его дизассемблированный код.

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 

Однако это может быть недостатком, если вы планируете запутывать свое программное обеспечение. После обфускации встроенная строка может больше не соответствовать имени элемента. Механизмы, которые полагаются на этот текст, сломаются. Примерами этого являются, в числе прочих, следующие: Reflection, NotifyPropertyChanged ...

Определение имени во время выполнения требует некоторой производительности, но является безопасным для обфускации. Если обфускация не требуется и не планируется, я бы рекомендовал использовать оператор nameof.

4

The MSDN article содержит список маршрутов MVC (пример, который действительно нажал на концепцию для меня) среди нескольких других. (Отформатированный) описание пункт гласит:

  • Когда сообщения об ошибках в коде,
  • подключение Model-View-Controller (MVC) ссылки,
  • стрельба свойства изменилось событие, и т.д.,

вы часто хотите фиксирует имя строки метода.Использование nameof помогает сохранить код действительный при переименовании определений.

Перед тем, как был использовать строковые литералы обратиться к определениям, который является хрупким при переименовании элементов кода , потому что инструменты не знают, чтобы проверить эти строки литералов.

Принятые/высоко оцененные ответы уже дают несколько отличных конкретных примеров.

4

проект ASP.NET, MVC, Ядро использует nameof в AccountController.cs и ManageController.cs с методом RedirectToAction ссылаться на действие в контроллере.

Пример:

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

Это приводит к:

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

и берет берет пользователя к действию 'индекс' в контроллере 'Home', т.е. /Home/Index.

+0

Почему бы не пойти целиком и не использовать 'return RedirectToAction (nameof (HomeController.Index), nameof (HomeController) .Substring (nameof (HomeController), 0, nameof (HomeController) .Length-« Controller ».Length)); ' ? – Suncat2000

+1

@ Suncat2000 О, мальчик такой уродливый. – Fred

0

Один из использования nameof ключевого слова для установки Binding в МОФ программно.

, чтобы установить Binding, вы должны установить Path со строкой, а с ключевым словом nameof можно использовать опцию Refactor.

Например, если у вас есть IsEnable свойство зависимостей в вашем UserControl, и вы хотите, чтобы связать его с IsEnable некоторых CheckBox в вашем UserControl, вы можете использовать эти два кода:

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

и

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

Очевидно, что первый код не может реорганизовать, но секонд ...

0

Раньше мы использовали что-то вроде этого:

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

Причина - скомпилировать время безопасности. Никто не может молча переименовать свойство и нарушить логику кода. Теперь мы можем использовать nameof().

+0

Но nameof работает также для локальных переменных, полей, методов ... –

7

Здесь есть много хороших ответов. Я хотел бы добавить еще одну утилиту, где эта функция nameof C# 6.0 становится удобной. Рассмотрим библиотеку, такую ​​как Dapper, которая облегчает извлечение DB. Хотя это отличная библиотека, вам нужно жестко указать имена свойств в запросе, что не позволяет компилятору находить неправильные имена свойств. Благодаря функции интерполяции строк и функций nameof она становится намного проще и типизируется.

Из примера, приведенного в ссылке

без nameof

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

с nameof

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

Смежные вопросы