День двести тридцать четвёртый. #ЗаметкиНаПолях

Использование nameof

С точки зрения синтаксиса, оператор nameof похож на typeof, за исключением того, что идентификатор в скобках не обязательно должен быть типом. Почему же лучше использовать nameof вместо строковых литералов? Это надёжнее. Если вы сделаете опечатку в строковом литерале, вам нечего укажет на неё, тогда как, если вы сделаете опечатку в имени операнда, вы получите ошибку во время компиляции. Кроме того, компилятор знает, что аргумент nameof связан с элементом или переменной. Если вы переименуете переменную, используя инструмент рефакторинга, имя аргумента тоже изменится. Однако заметьте, что компилятор по-прежнему не сможет определить проблему, если вы ссылаетесь на другой элемент с похожим именем.



Примеры использования

1. Валидация аргументов

При программировании по контракту каждый метод проверяет входящие данные. Это называется предусловиями. Например, далее используется статический метод класса Preconditions для проверки аргумента на null:

public void SomeMethod(string name)

{

Preconditions.CheckNotNull(name, nameof(name));



}

2. Уведомление об изменении калькулируемого свойства

Предположим, что есть класс Rectangle со свойствами Height и Width для чтения и записи и свойство только для чтения Area. Полезно иметь возможность вызвать событие для свойства Area и указать имя свойства безопасным способом:

public class Rectangle : INotifyPropertyChanged

{

public event PropertyChangedEventHandler PropertyChanged;

public double Width

{

get { … }

set

{



RaisePropertyChanged();

RaisePropertyChanged(nameof(Area));

}

}



}

3. Атрибуты

Иногда атрибуты ссылаются на другие члены, чтобы обозначить, как члены связаны друг с другом. Это используется, например, в MVC DataAnnotations, где можно с помощью атрибутов обозначать ограничения, накладываемые на модель, например, требование обязательности поля, если поставлен флажок. NUnit позволяет параметризировать тесты значениями из поля или свойства, используя атрибут TestCaseSource. В Entity Framework достаточно распространено иметь в классе два свойства: одно для внешнего ключа, а другое - для сущности, которую представляет этот ключ:

public class Employee

{

[ForeignKey(nameof(Employer))]

public Guid EmployerId { get; set; }

public Company Employer { get; set; }

}



Особенности использования nameof

1. Указание членов других типов

Стоит помнить, что nameof возвращает только имя члена, без имени класса. Например, nameof(Cultures.AllCultures) вернёт "AllCultures"

2. Обобщения

При использовании typeof, и typeof(List<string>), и typeof(List<>) допустимы и выдают разные результаты. При использовании nameof, во-первых, нельзя использовать тип без аргумента типа nameof(List<>), а во-вторых, и nameof(Action<string>) и nameof(Action<string, string>) вернут просто "Action".

Кроме того, тип параметра не выводится во время выполнения, а используется просто имя типа параметра. То есть для

static string Method<T>() => nameof(T);

И Method<Guid>() и Method<Button>() вернут просто "T".

3. Использование псевдонимов

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

using GuidAlias = System.Guid;



Console.WriteLine(nameof(GuidAlias));

4. Предопределённые псевдонимы, массивы и обнуляемые значимые типы

Оператор nameof не может быть использован с предопределёнными псевдонимами (int, char, long, и т.п.), с суффиксом ? у обнуляемых типов или с массивами. То есть все следующие варианты не будут компилироваться:

nameof(float)

nameof(Guid?)

nameof(String[])



Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 9.