Какова цель имени?

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

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

Любая идея о цели?

17 ответов

Решение

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

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

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

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

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

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

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

Это действительно полезно для 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 содержит значение enum (т.е. 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....");
}

Еще один вариант использования, где 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 });

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

например.

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

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


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

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

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

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

//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 (C# и Visual Basic Reference)

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

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

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

Вместо этого, если у вас было

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

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

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

Пример:

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

Это переводится как:

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

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

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

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

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

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

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

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

Цель 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"
}

Я использовал его для именования ресурсов:

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

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

Цель nameof - рефакторинг. Например, если вы измените имя класса, на который вы ссылаетесь через nameof в другом месте вашего кода, вы получите ошибку компиляции, которая вам и нужна. Если бы вы не использовали nameof, вам пришлось бы искать имя класса в полнотекстовом поиске, чтобы изменить его. Это нижняя боль. С nameoff вы можете отдыхать спокойно, создавать и получать все кейсы для изменений автоматически через вашу среду IDE.

Я нахожу это 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 },
    }
}

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

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

Менее грязный:)

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

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

Это имеет преимущество при использовании ASP.Net MVC. Когда вы используете помощник HTML для создания некоторого элемента управления, он использует имена свойств в атрибуте name входного файла 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)}" })
  }
}

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

Одно из использования 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);

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

Другие вопросы по тегам