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

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

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

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

14 ответов


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

рассмотрим пример:

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

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

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

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

(A очень хорошая статья от Эрика Липперта почему 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"

это на самом деле довольно медленно .Объем имеет значение enum (т. е. 7) и находит имя во время выполнения.

вместо того, чтобы использовать nameof:

Console.WriteLine(nameof(MyEnum.FooBar))

> "FooBar"

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


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

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

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

например.

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

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


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

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


другой случай, где nameof функция C# 6.0 становится удобной - рассмотреть библиотеку как щеголеватый что делает DB извлекает гораздо проще. Хотя это отличная библиотека, вам нужно жестко кодировать имена свойств/полей в запросе. Это означает, что если вы решите переименовать свое свойство/поле, есть высокая вероятность того, что вы забудете обновить запрос для использования новых имен полей. С интерполяцией строк и 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 });


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

Читайте также: nameof (Справочник по C# и Visual Basic)


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

пример:

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

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

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

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


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

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

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

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

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


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

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

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

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

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

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


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

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

вы часто хотите захват string имя метода. Использование 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 ключевое слово для установки Binding в wpf программно.

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

например, если у вас есть 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);

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


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

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

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


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

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