Разница между nameof и typeof

Поправьте меня, если я ошибаюсь, но делаю что-то вроде

var typeOfName = typeof(Foo).Name;

и

var nameOfName = nameof(Foo);

должен дать вам точно такой же результат. Одна из понятных причин согласно этому источнику: https://msdn.microsoft.com/en-us/library/dn986596.aspx это

«Использование nameof помогает сохранить ваш код действительным при переименовании определений»

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

var fooInstance = new Foo();
var nameOfName = nameof(fooInstance);

однако вы можете сделать что-то вроде:

static string GetName<T>(T item) where T : class 
{
  return typeof(T).GetProperties()[0].Name;
}
var typeOfName2 = GetName(new { fooInstance });

В обоих случаях (typeof и nameof) возможен рефакторинг, поэтому я не вижу другой причины для повторного изобретения другого ключевого слова более высокого уровня, такого как nameof, для выполнения того, что уже существует. Есть ли между ними различия, которых я не вижу?

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

Обновление 1: взято из здесь

nameof, по-видимому, так же эффективно, как объявление строковой переменной. Никаких отражений и прочего!

var firstname = "Gigi";
 var varname = nameof(firstname);
 Console.WriteLine(varname); // Prints "firstname" to the console

Когда вы проверите сгенерированный MSIL, вы увидите, что он эквивалентен объявлению строки, потому что ссылка объекта на строку помещается в стек с помощью оператора ldstr:

IL_0001: ldstr "Gigi"
IL_0006: stloc.0
IL_0007: ldstr "firstname"
IL_000c: stloc.1
IL_000d: ldloc.1
IL_000e: call void [mscorlib]System.Console::WriteLine(string)

person Community    schedule 11.11.2015    source источник
comment
nameof(fooInstance) возвращает "fooInstance". С nameof вы получаете имя переменной, а не имя типа.   -  person Gentian Kasa    schedule 11.11.2015
comment
он получает строковую версию своей переменной. nameof(aaa) вернет aaa   -  person Suren Srapyan    schedule 11.11.2015
comment
static string GetName<T>(T item) делает что-то совершенно отличное от typeof или nameof. Он возвращает имя случайного свойства, которое имеет тип. Он также может вызвать исключение, если он используется не так, как предполагалось.   -  person Dennis_E    schedule 11.11.2015
comment
Я бы предпочел использовать nameof, чем добавлять свой собственный метод с анонимными типами.   -  person DavidG    schedule 11.11.2015
comment
nameof и typeof не являются функциями   -  person Ivan Stoev    schedule 11.11.2015
comment
Я имел в виду, что они являются функциями времени компиляции, поэтому вы не увидите реализацию исходного кода. Ваши примеры - это функции времени выполнения.   -  person Ivan Stoev    schedule 11.11.2015


Ответы (7)


Две причины:

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

Во-вторых, он используется не только для имен типов. Например, аргументы:

void SomeMethod(int myArgument)
{
  Debug.WriteLine(nameof(myArgument));
}

Вы также можете получить имя членов класса и даже местных жителей. Излишне говорить, что это весьма полезно для отладки информации. Это также один из способов реализовать менее хрупкое отражение, когда, например. синтаксический анализ деревьев выражений (к сожалению, в проекте, где я буду использовать это, мы все еще застряли на .NET 4.0 с C # 5 - это спасло бы меня от нескольких хаков здесь и там).

И чтобы прояснить некоторую путаницу, nameof не является функцией, как и typeof. Это оператор времени компиляции, и он всегда оценивается во время компиляции (хотя очевидно, что дженерики перемещают «время компиляции» немного дальше во времени).

person Luaan    schedule 11.11.2015

Вот эталонный тест с использованием BenchmarkDotNet

// * Summary *

Host Process Environment Information:
BenchmarkDotNet.Core=v0.9.9.0
OS=Windows
Processor=?, ProcessorCount=8
Frequency=2740584 ticks, Resolution=364.8857 ns, Timer=TSC
CLR=CORE, Arch=64-bit ? [RyuJIT]
GC=Concurrent Workstation
dotnet cli version: 1.0.0-preview2-003133

Type=GetNameBenchmark  Mode=Throughput  LaunchCount=2
WarmupCount=10  TargetCount=200

     Method |     Median |    StdDev |
----------- |----------- |---------- |
     TypeOf | 16.0348 ns | 0.7896 ns |
     NameOf |  0.0005 ns | 0.0147 ns |
person Feiyu Zhou    schedule 20.12.2017
comment
Спасибо за указание на BenchmarkDotNet ... никогда не слышал о нем раньше - person KornMuffin; 05.01.2018

Использование Reflection для генерации строк возможно, но не очень элегантно и не всегда возможно. Например, вы не можете использовать Reflection в изолированном коде. И вы не можете использовать его для локальных переменных. И это дорого.

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

person Hans Passant    schedule 11.11.2015

Между ними есть несколько различий, но они в основном носят практический характер. Пример 1:

Более элегантно написать что-то вроде

switch (e.ArgumentPropertyName)
{
    case nameof(aProperty):
    break;

    case "anotherProperty":
    break;
}

Попробуйте рефакторить тип anotherProperty и бац! nameof отразит изменения, "anotherProperty" пройдет молча, и ваш код никогда не будет выполнен в этом операторе case..

Пример 2:

enum MetalEnum { Gold = 1,  Silver = 2, ... }

Какой лучше?

Console.WriteLine(MetalEnum.Gold.ToString()); // finds the name at runtime (slower)

or

Console.WriteLine(nameof(MetalEnum.Gold)); // compile time (faster)

Пример 3:

Наконец, вы помните, как некрасиво писать что-то вроде

PropertyChanged(this, new PropertyChangedEventArgs("Foo"));

Теперь можно так же красиво написать:

PropertyChanged(this, new PropertyChangedEventArgs(nameof(Foo)));
person Greizzer    schedule 11.11.2015

Typeof возвращает объекты Type. Он часто используется как параметр или как переменная или поле. Оператор typeof является частью выражения, которое получает указатель Type.

class Program
{
    static Type _type = typeof(char); // Store Type as field.

    static void Main()
    {
    Console.WriteLine(_type); // Value type pointer
    Console.WriteLine(typeof(int)); // Value type
    Console.WriteLine(typeof(byte)); // Value type
    Console.WriteLine(typeof(Stream)); // Class type
    Console.WriteLine(typeof(TextWriter)); // Class type
    Console.WriteLine(typeof(Array)); // Class type
    Console.WriteLine(typeof(int[])); // Array reference type
    }
}

Выход

System.Char
System.Int32
System.Byte
System.IO.Stream
System.IO.TextWriter
System.Array
System.Int32[]

Тем временем Nameof возвращает строку с именем переменной. Он работает во время компиляции. Это специальная функция компилятора, упрощающая некоторые программы.

int size=100;
Console.WriteLine(nameof(size));

выход:размер

person Bilal Sohail    schedule 11.11.2015

Согласно документации:

Используется для получения простого (неполного) строкового имени переменной, типа или члена.

...

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

Поскольку синтаксически аргумент должен быть выражением, есть много запрещенных вещей, которые бесполезно перечислять. Следует упомянуть следующее, вызывающее ошибки: предопределенные типы (например, int или void), типы, допускающие значение NULL (Point?), типы массивов (Customer[,]), типы указателей (Buffer*). ), квалифицированный псевдоним (A::B) и несвязанные универсальные типы (Dictionary<,>), символы предварительной обработки (DEBUG) и метки (loop:).

Простое имя, которое получает nameof, является именем источника, а не именем метаданных.

Итак, этот код:

using Integer = System.Int32;
var s = "a string";
Console.WriteLine(nameof(s));
Console.WriteLine(nameof(Integer));
Console.WriteLine(nameof(System.Int32));
void M<T>() { Console.WriteLine(nameof(T)); }
M<int>();
M<string>();

будет печатать:

s
Integer
Int32
T
T
person Paulo Morgado    schedule 11.11.2015

Typeof возвращает объекты типа. Он часто используется как параметр или как переменная или поле. Оператор typeof является частью выражения, которое получает указатель Type.

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

person Nimmy Alphonsa Jose    schedule 11.11.2015