Разница между 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
. Использует ли Reflection?
Обновление 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)
Ответы
Ответ 1
Две причины:
nameof
превращается в константу времени компиляции. typeof(...).Name
требует немного отражения. Это не слишком дорого, но в некоторых случаях это может повредить.
Во-вторых, он используется для других вещей, кроме имен типов. Например, аргументы:
void SomeMethod(int myArgument)
{
Debug.WriteLine(nameof(myArgument));
}
Вы также можете получить имя членов класса и даже локальных жителей. Излишне говорить, что это очень полезно для отладки информации. Это также один из способов реализации менее хрупкого отражения, когда, например, синтаксический анализ деревьев выражений (к сожалению, в проекте, где я буду использовать это, мы все еще придерживаемся .NET 4.0 с С# 5 - это спасло бы меня от нескольких хаков здесь и там).
И чтобы устранить некоторую путаницу, nameof
не является функцией, а также не является typeof
. Это оператор времени компиляции, и он всегда оценивается во время компиляции (хотя, очевидно, generics быстрее перемещают "время компиляции" ).
Ответ 2
Использование Reflection для генерации строк возможно, но не очень изящно и не всегда возможно. Например, вы не можете использовать Reflection в изолированном коде. И вы не можете использовать его для локальных переменных. И это дорого.
Оператор nameof
работает во время компиляции. Компилятор уже знает имя, когда он анализирует код. Таким образом, можно тривиально генерировать строковый литерал. Очень быстро, не может быть быстрее, и никаких ограничений времени выполнения.
Ответ 3
Между ними существует несколько различий, но в основном это практические причины.
Пример 1:
Более элегантно писать что-то вроде
switch (e.ArgumentPropertyName)
{
case nameof(aProperty):
break;
case "anotherProperty":
break;
}
Попробуйте реорганизовать тип anotherProperty
и удар! nameof
будет отражать изменения, "anotherProperty"
пройдет молча, и ваш код никогда не будет выполнен в этом случае.
Пример 2:
enum MetalEnum { Gold = 1, Silver = 2, ... }
Какой из них лучше?
Console.WriteLine(MetalEnum.Gold.ToString()); // finds the name at runtime (slower)
или
Console.WriteLine(nameof(MetalEnum.Gold)); // compile time (faster)
Пример 3:
Наконец, вы помните, как уродливо писать что-то вроде
PropertyChanged(this, new PropertyChangedEventArgs("Foo"));
Теперь вы можете писать так красиво, как показано ниже:
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Foo)));
Ответ 4
Здесь тестовый тест с использованием 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 |
Ответ 5
Тип возвращает объекты типа. Он часто используется как параметр или как переменная или поле. Оператор typeof является частью выражения, которое получает указатель типа.
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));
вывод: размер
Ответ 6
В соответствии с документация:
Используется для получения простого (неквалифицированного) имени строки для переменной, типа или члена.
...
Аргументом для nameof должно быть простое имя, квалифицированное имя, доступ к члену, базовый доступ с указанным членом или этот доступ с указанным членом. Выражение аргумента идентифицирует определение кода, но оно никогда не оценивается.
Поскольку аргумент должен быть синтаксическим выражением, существует множество вещей, которые не могут быть перечислены. Следует упомянуть следующие ошибки: предопределенные типы (например, int или void), типы с нулевым значением (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
Ответ 7
Typeof возвращает объекты типа. Он часто используется как параметр или как переменная или поле. Оператор typeof является частью выражения, которое получает указатель типа.
Nameof, тем временем, возвращает строку с именем переменной. Он работает во время компиляции. Это специальная функция компилятора, которая упрощает некоторые программы.