Apakah nameof () dievaluasi pada waktu kompilasi?

114

Di C # 6, Anda dapat menggunakan nameof()operator untuk mendapatkan string yang berisi nama variabel atau tipe.

Apakah ini dievaluasi pada waktu kompilasi, atau saat runtime melalui beberapa API Roslyn?

Gigi
sumber
Roslyn adalah platform kompilator baru. Ini hanya digunakan pada waktu kompilasi.
Paulo Morgado
2
@PauloMorgado itu tidak benar, Anda dapat menggunakan Rosyln pada saat berjalan untuk melakukan sesuatu. Seperti membangun editor kode langsung atau menggunakan penguraian Rosyln untuk melakukan sesuatu dengan pohon atau ekspresi atau sesuatu
Chris Marisic
@ChrisMarisic itu kesan saya, tapi saya tidak merespon karena pengetahuan saya tentang topik terbatas (karena itu pertanyaan saya). Saya menemukan ini: scriptcs.net yang merupakan contoh yang cukup bagus dari kekuatan Roslyn, dan yang saya yakini melakukan hal-hal runtime, tetapi saya bisa saja salah karena saya tidak cukup mendapat informasi tentangnya.
Gigi
@ChrisMarisic, jadi, apa yang Anda katakan adalah bahwa Anda dapat menggunakan Roslyn untuk membuat kode langsung dari sumber, bukan dari satu biner yang sedang berjalan. Dan Anda masih menggunakan Roslyn untuk mengubah source menjadi binari yang tidak akan menggunakan Roslyn untuk mengubah binries tersebut. Jika Anda tidak dapat menggunakan Roslyn secara permanen saat runtime, maka Anda tidak akan pernah dapat mengkompilasi kode apa pun.
Paulo Morgado

Jawaban:

119

Iya. nameof()dievaluasi pada waktu kompilasi. Melihat versi terbaru dari spesifikasi:

Nama ekspresi adalah konstanta. Dalam semua kasus, nameof (...) dievaluasi pada waktu kompilasi untuk menghasilkan string. Argumennya tidak dievaluasi pada waktu proses, dan dianggap sebagai kode yang tidak dapat dijangkau (namun tidak mengeluarkan peringatan "kode tidak dapat dijangkau").

Dari nameof operator - v5

Anda dapat melihatnya dengan contoh TryRoslyn ini di mana ini:

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

Dikompilasi dan diuraikan menjadi ini:

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

Yang setara dengan run-time adalah:

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

Seperti yang disebutkan dalam komentar, itu berarti bahwa ketika Anda menggunakan nameofparameter tipe dalam tipe generik, jangan berharap untuk mendapatkan nama tipe dinamis aktual yang digunakan sebagai parameter tipe alih-alih hanya nama parameter tipe. Jadi ini:

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

Akan menjadi ini:

public class Foo
{
    public void Bar<T>()
    {
        Console.WriteLine("T");
    }
}
i3arnon
sumber
Apa yang dimaksud dengan "waktu kompilasi" di sini? Kompilasi ke MSIL atau kompilasi ke kode asli?
pengguna541686
6
@Mehrdad Kompilator C # menghasilkan IL.
i3arnon
3
Pertanyaan singkat, dapatkah saya menggunakan nameof dalam kasus sakelar?
Eja
2
@ Eja Ya
i3arnon
58

Saya ingin memperkaya jawaban yang diberikan oleh @ I3arnon dengan bukti bahwa jawaban itu dievaluasi pada waktu kompilasi.

Mari kita asumsikan saya ingin mencetak nama variabel di Konsol menggunakan nameofoperator:

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

Ketika Anda memeriksa MSIL yang dihasilkan, Anda akan melihat bahwa itu setara dengan deklarasi string karena referensi objek ke string didorong ke tumpukan menggunakan ldstroperator:

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)

Anda akan melihat bahwa mendeklarasikan string nama depan dan menggunakan nameofoperator menghasilkan kode yang sama di MSIL, yang artinya nameofseefisien mendeklarasikan variabel string.

Faris Zacina
sumber
4
Jika MSIL didekompilasi menjadi kode sumber, seberapa mudah bagi decompiler untuk mengenalinya sebagai nameofoperator, bukan string hardcode biasa?
ADTC
11
Itu pertanyaan yang bagus! Anda dapat mempostingnya sebagai pertanyaan baru di SO jika Anda ingin mendapatkan penjelasan rinci :) .. namun jawaban singkatnya adalah bahwa decompiler tidak akan dapat mengetahui itu adalah nama operator, tetapi akan menggunakan string literal sebagai gantinya . Saya telah memverifikasi bahwa kasus dengan ILSpy dan Reflector.
Faris Zacina
2
@ ADTC: Karena nameof sepenuhnya diganti dengan load-a-string-into-the-stack, bagaimana mungkin decompiler mencoba menebak bahwa itu adalah nameof, dan bukan parameter konstan sederhana?
quetzalcoatl
2
Itu menarik. Mungkin decompiler dapat memeriksa string terhadap konteks saat ini (nama metode / properti / dll tempat Anda berada). Namun, tidak mungkin itu 100% dapat diandalkan - Anda mungkin telah menggunakan string hardcode.
Gigi
2
Meskipun saya setuju bahwa Anda tidak dapat mengetahui apakah itu nameof setelah kompilasi, saya tidak melihat indikasi bahwa ILSpy atau Reflector mendukung C # 6. Jika itu masalahnya, Anda tidak dapat mengujinya @TheMinister
Millie Smith