Чи оцінюється 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.