Какова цель nameof?

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

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

Есть идеи о цели?


person atikot    schedule 29.07.2015    source источник
comment
См. Также msdn.microsoft.com/library/dn986596.aspx   -  person Corak    schedule 29.07.2015
comment
Раньше не было способа получить это T. Раньше был способ получить использованный шрифт.   -  person Jon Hanna    schedule 29.07.2015
comment
Определенно полезно при рефакторинге / переименовании имени в nameof. Также помогает предотвратить опечатки.   -  person bvj    schedule 11.05.2017
comment
nameof пригодится с nunit 3 TestCaseData. В документации имя метода отображается строкой [Test, TestCaseSource(typeof(MyDataClass), "TestCases")], которую можно заменить на [Test, TestCaseSource(typeof(MyDataClass), nameof(MyDataClass.TestCases))]. Намного приятнее.   -  person Jeremy    schedule 05.03.2018
comment
Официальная документация nameof перемещена сюда: docs.microsoft.com/en-us/dotnet/csharp/language-reference/ - здесь также перечислены ключевые варианты использования, которые служат довольно хорошим ответом на вопрос.   -  person markus s    schedule 20.04.2018


Ответы (16)


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

Возьмем этот пример:

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

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

В первом случае переименование SomeProperty вызовет ошибку компиляции, если вы не измените и определение свойства, и выражение nameof(SomeProperty). Во втором случае переименование SomeOtherProperty или изменение строки "SomeOtherProperty" приведет к незаметному нарушению поведения во время выполнения без ошибок или предупреждений во время сборки.

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

(очень хорошая статья из Эрик Липперт, почему infoof не попал, а nameof -)

person Patrick Hofman    schedule 29.07.2015
comment
Я понимаю суть проблемы, просто добавив, что resharper изменяет строки при рефакторинге имен, не уверен, имеет ли VS аналогичную функциональность. - person Ash Burlaczenko; 29.07.2015
comment
Она имеет. Но и Resharper, и VS, например, не работают над проектами. Это делает. На самом деле это лучшее решение. - person Patrick Hofman; 29.07.2015
comment
Другой распространенный вариант использования - маршрутизация в MVC с использованием nameof и имени действия вместо жестко заданной строки. - person RJ Cuthbertson; 29.07.2015
comment
@RJCuthbertson Очень логично, но как насчет использования nameof в представлениях для имени действия? Класс контроллера не является статическим, поэтому мы не можем получить имя действия, какое-либо простое решение для этого тоже ?? - person sotn; 24.10.2016
comment
@sotn Я не уверен, что понимаю, о чем вы спрашиваете. Ничто не мешает вам использовать его как public class MyController { public ActionResult Index() { return View(nameof(Index)); } } - и вы можете использовать nameof для нестатических членов (например, вы можете вызвать nameof(MyController.Index), используя класс выше, и он будет генерировать Index). Ознакомьтесь с примерами на msdn.microsoft.com/ en-us / library / - person RJ Cuthbertson; 24.10.2016
comment
@RJCuthbertson wow доступ к нестатическому имени метода без экземпляра ?? это какая-то хардкорная логика компилятора, лол - person sotn; 25.10.2016
comment
Я не понимаю, почему это особенное. Имена переменных всегда одинаковы, верно? Независимо от того, есть у вас экземпляр или нет, имена переменных не изменятся @sotn - person Patrick Hofman; 25.10.2016
comment
@PatrickHofman Возможно, имена переменных, но как насчет имен методов? Вы также можете использовать nameof с именами методов. Предположим, что у меня есть действие в приложении MVC с именем Index, и я хочу изменить его имя, потому что я хочу, чтобы мои пользователи получали доступ к сайту как '/ MyController / Home /', а также не хочу делать это с помощью атрибута маршрута и просто переименуйте его в Home. Поиск Index и замена его на Home во всем проекте может занять много времени. - person sotn; 25.10.2016
comment
@PatrickHofman И я не уверен, что понимаю, что вы описали. Но если вы имели в виду, что такого особенного в использовании nameof в именах нестатических методов? Ну, например, в файле cshtml; у нас нет доступа к экземпляру контроллера, и мы используем множество URL-адресов и т. д., используя UrlHelpers, например Url.Action("Index") и т. д. Следовательно, это означает, что мы можем использовать Url.Action(nameof(HomeController.Index)). И когда мы переименуем его в будущем и включим компиляцию представления, мы сможем легко переименовать имена действий .. Использование его без экземпляра - это большая проблема .. - person sotn; 25.10.2016
comment
Нет, если вы понимаете, что все это делается во время компиляции. В то время еще нет экземпляров, просто введите @sotn - person Patrick Hofman; 25.10.2016
comment
@PatrickHofman, лол, конечно, это так. nameof не влияет на сгенерированный IL и т. д. Но я думаю, что это приятная мелочь, которая может немного облегчить вашу работу .. - person sotn; 25.10.2016
comment
Если кому-то интересно, что произойдет, если SomeProperty будет переименован в SomeOtherProperty, вы получите ошибку компиляции: docs.microsoft.com/en-us/dotnet/csharp/misc/cs0152 - person JohnLBevan; 02.10.2018

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

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

Теперь, если кто-то изменит имя параметра 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));
    }
}

Or...

// Write a log, audit or trace for the method called
void DoSomething(... params ...)
{
    Log(nameof(DoSomething), "Message....");
}
person Keith    schedule 29.07.2015
comment
И вы добавили еще одну интересную функцию: интерполяцию строк! - person Patrick Hofman; 29.07.2015
comment
@PatrickHofman и typeof(T), это еще один кусок сахара времени компиляции, который полезен в аналогичных обстоятельствах :-) - person Keith; 29.07.2015
comment
Одна вещь, которую мне не хватает, - это что-то вроде nameofthismethod. Вы можете использовать Log.Error($"Error in {nameof(DoSomething)}..."), но если вы скопируете и вставите это в другие методы, вы не заметите, что это все еще относится к DoSomething. Таким образом, хотя он отлично работает с локальными переменными или параметрами, имя метода является проблемой. - person Tim Schmelter; 21.07.2016
comment
Вы знаете, будет ли nameOf использовать атрибут [DisplayName], если он присутствует? В примере enum я часто использую [DisplayName] с проектами MVC. - person Luke T O'Brien; 15.06.2017
comment
@Keith Что ты узнал? Также есть атрибут [Description]. Было бы неплохо указать аргументы для nameOf, например, для решения вернуть тип FullName вместо Name при использовании nameOf(System.String) - person Luke T O'Brien; 15.06.2017
comment
@ LukeTO'Brien а, нет, я думал, ты говоришь, что это так. Я думаю, что nameof всегда получает настоящее имя, игнорируя любые атрибуты. - person Keith; 15.06.2017
comment
К сожалению, разработчик попытался следовать первому шаблону ArgumentException, но обобщил его на все исключения, поэтому все мои блоки catch залиты throw new Exception(nameof(ex)), который скрывает фактическое исключение и просто регистрирует ex. В ответе есть хорошие примеры, но убедитесь, что вы их понимаете, прежде чем пытаться применить. - person AaronLS; 24.07.2019
comment
@AaronLS Да, он довольно специализированный и не то, чем вы бы часто пользовались. Однако этот throw new - совершенно другой антипаттерн - я считаю, что чрезмерное использование catch является общей проблемой для младших разработчиков, потому что это похоже на решение проблемы (когда большую часть времени он просто скрывает ее). - person Keith; 24.07.2019

Другой вариант использования, когда nameof функция C # 6.0 становится удобной. Рассмотрим библиотеку, например Dapper, который значительно упрощает получение БД. Несмотря на то, что это отличная библиотека, вам необходимо жестко указать имена свойств / полей в запросе. Это означает, что если вы решите переименовать свое свойство / поле, высока вероятность того, что вы забудете обновить запрос, чтобы использовать новые имена полей. Благодаря интерполяции строк и функциям nameof код становится намного проще в обслуживании и становится безопасным с точки зрения типов.

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

без имени

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

с именем

var dog = connection.Query<Dog>($"select {nameof(Dog.Age)} = @Age, {nameof(Dog.Id)} = @Id", new { Age = (int?)null, Id = guid });
person Sateesh Pagolu    schedule 16.11.2016
comment
Мне нравится Dapper, и мне очень нравятся строковые интерпорляции, но, IMO, это выглядит так некрасиво. Риск нарушить запрос путем переименования столбца кажется таким маленьким по сравнению с такими уродливыми запросами. На первый взгляд я бы сказал, что предпочитаю писать запросы EF LINQ или следовать соглашению вроде [TableName]. [ColumnName], где я могу легко находить / заменять свои запросы, когда мне нужно. - person drizin; 14.11.2019
comment
@drizin Я использую Dapper FluentMap для предотвращения таких запросов (а также для разделения проблем) - person mamuesstack; 21.04.2020

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

Например:

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

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


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

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

person Jodrell    schedule 29.07.2015
comment
для регистрации я мог бы просто написать имя переменной сам, было бы короче - person atikot; 29.07.2015
comment
@atikot: Но тогда, если вы переименуете переменную, компилятор не заметит, что строка больше не совпадает. - person O. R. Mapper; 29.07.2015
comment
Я действительно использую resharper, который учитывает это, но я понимаю вашу точку зрения. - person atikot; 29.07.2015
comment
@atikot, я тоже, но Resharper выдает только предупреждение, а не ошибку компилятора. Есть разница между уверенностью и хорошим советом. - person Jodrell; 29.07.2015
comment
@atikot, и Resharper не проверяет сообщения журнала - person Jodrell; 29.07.2015
comment
@Jodrell: И, я подозреваю, он также не проверяет различные другие варианты использования - как насчет привязок WPF, созданных в коде программной части, пользовательских методов OnPropertyChanged (которые напрямую принимают имена свойств, а не PropertyChangedEventArgs) или вызовов отражения для поиска конкретный член или тип? - person O. R. Mapper; 29.07.2015

Самый распространенный вариант использования, который я могу придумать, - это работа с интерфейсом 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.

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

person Roy T.    schedule 29.07.2015
comment
Хотя это допустимый подход, более удобный (и СУХИЙ) подход - использовать атрибут [CallerMemberName] в параметре нового метода, чтобы вызвать это событие. - person Drew Noakes; 30.07.2015
comment
Я согласен, что CallerMemberName тоже хорош, но это отдельный вариант использования, поскольку (как вы сказали) вы можете использовать его только в методах. Что касается DRY, я не уверен, что [CallerMemberName]string x = null лучше, чем nameof(Property). Можно сказать, что имя свойства используется дважды, но в основном это аргумент, передаваемый функции. Думаю, не совсем то, что подразумевается под DRY :). - person Roy T.; 30.07.2015
comment
Фактически вы можете использовать это в свойствах. Они тоже члены. Преимущество перед nameof заключается в том, что установщику свойств вообще не нужно указывать имя свойства, что исключает возможность ошибок копирования / вставки. - person Drew Noakes; 30.07.2015
comment
Это ситуация «лучше вместе» для INotifyPropertyChanged, использование [CallerMemberNameAttribute] позволяет аккуратно поднять уведомление об изменении из установщика свойств, в то время как синтаксис nameof позволяет чисто поднять уведомление об изменении из другого места в вашем коде. - person Andrew Hanlon; 30.07.2015

Чаще всего используется проверка ввода, например

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

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

См. Также: nameof (Справочник по C # и Visual Basic)

person Massimo Prota    schedule 29.07.2015

В проекте ASP.NET Core MVC используется nameof в _ 2_ и _ 3_ с RedirectToAction методом для ссылки на действие в контроллере.

Пример:

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

Это означает:

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

и переводит пользователя к действию «Индекс» в контроллере «Домой», то есть /Home/Index.

person Fred    schedule 22.04.2016
comment
Почему бы не съесть целиком и не использовать return RedirectToAction(nameof(HomeController.Index), nameof(HomeController).Substring(nameof(HomeController),0,nameof(HomeController).Length-"Controller".Length));? - person Suncat2000; 03.11.2017
comment
@ Suncat2000, потому что одна из этих вещей выполняется при компиляции, а другая - нет? :) - person Dinerdo; 16.08.2018

Допустим, вам нужно напечатать имя переменной в вашем коде. Если вы напишете:

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

а затем, если кто-то реорганизует код и использует другое имя для myVar, ему / ей придется искать строковое значение в вашем коде и соответственно изменять его.

Вместо этого, если вы напишете:

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

Это помогло бы автоматически выполнить рефакторинг!

person cnom    schedule 29.07.2015
comment
Мне жаль, что не было специального синтаксиса переменных параметров, который передавал бы массив кортежей, по одному для каждого параметра, содержащий представление исходного кода, Type и значение. Это позволило бы коду, вызывающему методы ведения журнала, устранить излишнюю избыточность. - person supercat; 29.07.2015

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

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

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

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

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

person brichins    schedule 05.08.2015

Как уже отмечали другие, оператор 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.

person cel sharp    schedule 01.08.2015

Цель оператора 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"

Or:

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

Одно из применений, которое я давал ему, - это именование ресурсов:

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

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

person Paulo Morgado    schedule 31.07.2015

Другой вариант использования nameof - это проверка страниц вкладок, вместо проверки индекса вы можете проверить свойство Name страниц вкладок следующим образом:

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

Менее беспорядочно :)

person diedrop    schedule 01.04.2019

Я считаю, что nameof увеличивает удобочитаемость очень длинных и сложных операторов SQL в моих приложениях. Это выделяет переменные из этого моря строк и избавляет вас от необходимости выяснять, где переменные используются в ваших операторах 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 },
    }
}
person Kristianne Nerona    schedule 16.01.2020

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

чтобы установить 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);

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

person Mehdi Khademloo    schedule 17.07.2016

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

// 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 ().

person QtRoS    schedule 26.07.2016

Это имеет преимущество при использовании ASP.Net MVC. Когда вы используете помощник HTML для создания некоторого элемента управления, он использует имена свойств в атрибуте имени ввода html:

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

Получается что-то вроде этого:

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

Итак, теперь, если вам нужно проверить свое свойство в методе Validate, вы можете сделать это:

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

Если вы переименуете свое свойство с помощью инструментов рефакторинга, ваша проверка не будет нарушена.

person dgtlfx    schedule 20.03.2018