Чи оцінюється nameof () під час компіляції?


114

У C # 6 ви можете використовувати nameof()оператор, щоб отримати рядок, що містить ім'я змінної чи типу.

Чи оцінюється це під час компіляції або під час виконання через якийсь API Roslyn?


Roslyn - нова платформа компілятора. Він використовується лише під час компіляції.
Пауло Моргадо

2
@PauloMorgado, що не відповідає дійсності, ви можете використовувати Rosyln під час виконання, щоб робити щось. Наприклад, створити редактор живого коду або використовувати матеріали для розбору Росільн для того, щоб робити речі з деревами або виразами чи щось подібне
Кріс Марісіч

@ChrisMarisic це моє враження, але я не відповів, оскільки мої знання з цієї теми обмежені (звідси моє запитання). Я натрапив на це: scriptcs.net, який є досить хорошим прикладом потужності Росліна, і, на мою думку, це робить час виконання, але я можу помилитися, оскільки я не дуже добре інформований про це.
Джіджі

@ChrisMarisic, то, що ви говорите, це те, що ви можете використовувати Roslyn для створення живого коду з джерела, а не з тієї бінарної, яка працює. І ви все ще використовуєте Roslyn для перетворення джерела в двійкові файли, які не користуються Roslyn для зміни бінарних файлів. Якщо ви не можете абослусливо використовувати Roslyn під час виконання, тоді ви ніколи не зможете скласти жоден код.
Пауло Моргадо

Відповіді:


119

Так. nameof()оцінюється під час компіляції. Переглядаючи останню версію специфікацій:

Вираз 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");
    }
}

Що тут "час компіляції"? Компіляція до MSIL чи компіляція до рідного коду?
користувач541686

6
@Mehrdad Компілятор C # генерує ІЛ.
i3arnon

3
Швидке запитання, чи можна використовувати nameof у корпусі комутатора?
Заклинання

2
@Spell Так
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
Це гарне запитання! Ви можете опублікувати його як нове запитання на ТАК, якщо ви хочете отримати детальне пояснення :) .. Однак коротка відповідь полягає в тому, що декомпілятор не зможе зрозуміти, що це оператор nameof, але використовуватиме замість нього рядковий літерал . Я перевірив, що це стосується ILSpy та Reflector.
Фаріс Зачіна

2
@ADTC: Коли nameof повністю замінено на load-a-string-on-the-stack, як декомпілятор може навіть спробувати вгадати, що це було nameof, а не простий постійний параметр?
quetzalcoatl

2
Це цікаво. Можливо, декомпілятор міг би перевірити рядок відповідно до поточного контексту (назва методу / властивості / тощо, в якому ви знаходитесь). Проте, це не може бути 100% надійним - ви, можливо, використовували жорстко закодовану рядок.
Джіджі

2
Хоча я погоджуюся, що ви не можете знати, чи це ім’я після компіляції, я не бачу жодних ознак того, що ILSpy або Reflector підтримують C # 6. Якщо це так, ви не можете перевірити його @TheMinister
Міллі Сміт,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.