Czy nameof () jest oceniane w czasie kompilacji?

114

W C # 6 możesz użyć nameof()operatora, aby uzyskać ciąg zawierający nazwę zmiennej lub typu.

Czy jest to oceniane w czasie kompilacji, czy w czasie wykonywania za pośrednictwem interfejsu API Roslyn?

Gigi
źródło
Roslyn to nowa platforma kompilatora. Jest używany tylko w czasie kompilacji.
Paulo Morgado
2
@PauloMorgado to nieprawda, możesz używać Rosyln w czasie wykonywania, aby robić różne rzeczy. Na przykład zbudowanie edytora kodu na żywo lub użycie parsowania Rosyln do robienia rzeczy z drzewami lub wyrażeniami lub czymś w tym rodzaju
Chris Marisic
@ChrisMarisic takie mam wrażenie, ale nie odpowiedziałem, bo moja wiedza na ten temat jest ograniczona (stąd moje pytanie). Natknąłem się na to: scriptcs.net, który jest całkiem dobrym przykładem potęgi Roslyn i który, jak sądzę, działa w środowisku wykonawczym, ale mogę się mylić, ponieważ nie jestem o tym dobrze poinformowany.
Gigi
@ChrisMarisic, więc mówisz, że możesz użyć Roslyna do zbudowania kodu na żywo ze źródła, a nie z jednego uruchomionego pliku binarnego. I nadal używasz Roslyna do przekształcania źródeł w pliki binarne, które nie będą używać Roslyna do zmiany tych plików. Jeśli nie możesz niezauważalnie używać Roslyn w czasie wykonywania, nigdy nie możesz skompilować żadnego kodu.
Paulo Morgado

Odpowiedzi:

119

Tak. nameof()jest oceniany w czasie kompilacji. Patrząc na najnowszą wersję specyfikacji:

Nazwa wyrażenia jest stałą. We wszystkich przypadkach nameof (...) jest oceniane w czasie kompilacji w celu utworzenia ciągu. Jego argument nie jest oceniany w czasie wykonywania i jest uważany za nieosiągalny kod (jednak nie emituje ostrzeżenia „nieosiągalny kod”).

Od operatora nameof - v5

Możesz to zobaczyć w tym przykładzie TryRoslyn, gdzie:

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

Jest kompilowany i dekompilowany do tego:

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

Jego odpowiednik w czasie wykonywania to:

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

Jak wspomniano w komentarzach, oznacza to, że gdy używasz nameofparametrów typu w typie ogólnym, nie spodziewaj się, że otrzymasz nazwę rzeczywistego typu dynamicznego używanego jako parametr typu, zamiast tylko nazwy parametru typu. Więc to:

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

Stanie się tym:

public class Foo
{
    public void Bar<T>()
    {
        Console.WriteLine("T");
    }
}
i3arnon
źródło
Co to jest „czas kompilacji”? Kompilacja do MSIL czy kompilacja do kodu natywnego?
user541686
6
@Mehrdad Kompilator C # generuje IL.
i3arnon
3
Szybkie pytanie, czy mogę używać nameof w obudowie przełącznika?
Przeliteruj
2
@Spell Yes
i3arnon
58

Chciałem wzbogacić odpowiedź udzieloną przez @ I3arnon o dowód, że jest oceniana w czasie kompilacji.

Załóżmy, że chcę wydrukować nazwę zmiennej w konsoli za pomocą nameofoperatora:

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

Kiedy sprawdzisz wygenerowany MSIL, zobaczysz, że jest on równoważny deklaracji ciągu, ponieważ odwołanie do obiektu do ciągu jest wypychane na stos za pomocą ldstroperatora:

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)

Zauważysz, że zadeklarowanie ciągu firstname i użycie nameofoperatora generuje ten sam kod w MSIL, co oznacza, że nameofjest tak samo wydajne, jak deklarowanie zmiennej łańcuchowej.

Faris Zacina
źródło
4
Jeśli MSIL jest dekompilowany do kodu źródłowego, jak łatwo będzie dekompilatorowi rozpoznać, że był to nameofoperator, a nie zwykły ciąg znaków zakodowanych na stałe?
ADTC
11
To dobre pytanie! możesz wysłać to jako nowe pytanie do SO, jeśli chcesz uzyskać szczegółowe wyjaśnienie :) .. jednak krótka odpowiedź jest taka, że ​​dekompilator nie będzie w stanie dowiedzieć się, że był to operator nameof, ale zamiast tego użyje literału ciągu . Sprawdziłem, że tak jest w przypadku ILSpy i Reflector.
Faris Zacina
2
@ADTC: Skoro nameof jest w pełni zastępowana przez load-a-string-on-the-stack, jak dekompilator mógłby nawet próbować odgadnąć, że to nameof, a nie prosty parametr stały?
quetzalcoatl
2
To jest interesujące. Być może dekompilator mógłby sprawdzić ciąg znaków w bieżącym kontekście (nazwa metody / właściwości / itp., W którym się znajdujesz). Mimo to nie ma sposobu, aby był w 100% niezawodny - w końcu mogłeś użyć zakodowanego na stałe ciągu.
Gigi
2
Chociaż zgadzam się, że nie możesz wiedzieć, czy to nameof po kompilacji, nie widzę żadnej wskazówki, że ILSpy lub Reflector obsługują jeszcze C # 6. Jeśli tak jest, nie możesz tego przetestować @TheMinister
Millie Smith