Оценивается ли nameof () во время компиляции?

114

В C # 6 вы можете использовать nameof()оператор для получения строки, содержащей имя переменной или типа.

Это оценивается во время компиляции или во время выполнения через какой-нибудь Roslyn API?

Gigi
источник
Roslyn - это новая платформа компилятора. Он используется только во время компиляции.
Пауло Моргадо
2
@PauloMorgado, это неправда, вы можете использовать Rosyln во время выполнения, чтобы что-то делать. Например, создание живого редактора кода или использование синтаксического анализа Rosyln для работы с деревьями или выражениями или что-то в этом роде
Крис Марисик
@ChrisMarisic, это мое впечатление, но я не ответил, так как мои знания по теме ограничены (отсюда и мой вопрос). Я наткнулся на это: scriptcs.net, который является довольно хорошим примером мощи Roslyn и который, как мне кажется, работает во время выполнения, но я могу ошибаться, поскольку я не очень хорошо осведомлен об этом.
Gigi
@ChrisMarisic, итак, вы говорите, что вы можете использовать Roslyn для создания живого кода из исходного кода, а не из одного исполняемого файла. И вы все еще используете Roslyn для преобразования исходного кода в двоичные файлы, которые не будут использовать Roslyn для изменения этих двоичных файлов. Если вы не можете полностью использовать Roslyn во время выполнения, вы никогда не сможете скомпилировать какой-либо код.
Пауло Моргадо

Ответы:

119

Да. nameof()оценивается во время компиляции. Глядя на последнюю версию спецификаций:

Выражение nameof является константой. Во всех случаях nameof (...) вычисляется во время компиляции для создания строки. Его аргумент не оценивается во время выполнения и считается недостижимым кодом (однако он не выдает предупреждения о «недостижимом коде»).

От имени оператора - v5

Вы можете увидеть это на примере TryRoslyn, где это:

public class Foo
{
    public void Bar()
    {
        Console.WriteLine(nameof(Foo));
    }
}

Скомпилировано и декомпилировано в это:

public class Foo
{
    public void Bar()
    {
        Console.WriteLine("Foo");
    }
}

Его эквивалент во время выполнения:

public class Foo
{
    public void Bar()
    {
        Console.WriteLine(typeof(Foo).Name);
    }
}

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

public class Foo
{
    public void Bar<T>()
    {
        Console.WriteLine(nameof(T));
    }
}

Станет таким:

public class Foo
{
    public void Bar<T>()
    {
        Console.WriteLine("T");
    }
}
i3arnon
источник
Что здесь «время компиляции»? Компиляция в MSIL или компиляция в собственный код?
user541686
6
@Mehrdad Компилятор C # генерирует IL.
i3arnon
3
Быстрый вопрос, могу ли я использовать nameof в случае коммутатора?
Spell
2
@Spell Yes
i3arnon
58

Я хотел обогатить ответ, предоставленный @ I3arnon, доказательством того, что он оценивается во время компиляции.

Предположим, я хочу вывести имя переменной в консоли с помощью 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)

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

Фарис Засина
источник
4
Если MSIL декомпилирован в исходный код, насколько легко будет декомпилятору распознать, что это был nameofоператор, а не простая жестко закодированная строка?
ADTC
11
Это хороший вопрос! вы можете опубликовать его как новый вопрос по SO, если хотите получить подробное объяснение :) .. однако короткий ответ заключается в том, что декомпилятор не сможет определить, что это был оператор nameof, а вместо этого будет использовать строковый литерал , Я подтвердил, что это относится к ILSpy и Reflector.
Фарис Засина
2
@ADTC: Поскольку nameof полностью заменяется на load-a-string-on-the-stack, как может декомпилятор даже попытаться угадать, что это nameof, а не простой постоянный параметр?
quetzalcoatl
2
Это интересно. Возможно, декомпилятор сможет проверить строку на соответствие текущему контексту (имя метода / свойства / и т. Д., В котором вы находитесь). Тем не менее, он не может быть на 100% надежным - возможно, вы все-таки использовали жестко запрограммированную строку.
Gigi
2
Хотя я согласен с тем, что вы не можете узнать, является ли это nameof после компиляции, я еще не вижу никаких признаков того, что ILSpy или Reflector поддерживают C # 6. Если это так, вы не можете проверить это @TheMinister
Милли Смит