Wird nameof () zur Kompilierungszeit ausgewertet?

114

In C # 6 können Sie den nameof()Operator verwenden, um eine Zeichenfolge abzurufen, die den Namen einer Variablen oder eines Typs enthält.

Wird dies zur Kompilierungszeit oder zur Laufzeit über eine Roslyn-API ausgewertet?

Gigi
quelle
Roslyn ist die neue Compiler-Plattform. Es wird nur zur Kompilierungszeit verwendet.
Paulo Morgado
2
@PauloMorgado das ist nicht wahr, Sie können Rosyln zur Laufzeit verwenden, um Dinge zu tun. Zum Beispiel einen Live-Code-Editor erstellen oder Rosylns Parsing-Zeug verwenden, um Dinge mit Bäumen oder Ausdrücken oder so etwas zu tun
Chris Marisic
@ ChrisMarisic das ist mein Eindruck, aber ich habe nicht geantwortet, da mein Wissen zu diesem Thema begrenzt ist (daher meine Frage). Ich bin auf Folgendes gestoßen : scriptcs.net , ein ziemlich gutes Beispiel für Roslyns Macht, und von dem ich glaube, dass es Laufzeit-Sachen macht, aber ich könnte mich irren, da ich nicht ganz gut darüber informiert bin.
Gigi
@ChrisMarisic, Sie sagen also, dass Sie Roslyn verwenden können, um Live-Code aus dem Quellcode zu erstellen, nicht aus der einen Binärdatei, die ausgeführt wird. Und Sie verwenden Roslyn immer noch, um die Quelle in Binärdateien umzuwandeln, die Roslyn nicht zum Ändern dieser Binärdateien verwenden. Wenn Sie Roslyn zur Laufzeit nicht unbedingt verwenden könnten, könnten Sie niemals Code kompilieren.
Paulo Morgado

Antworten:

119

Ja. nameof()wird zur Kompilierungszeit ausgewertet. Schauen Sie sich die neueste Version der Spezifikationen an:

Der Name des Ausdrucks ist eine Konstante. In allen Fällen wird nameof (...) zur Kompilierungszeit ausgewertet , um eine Zeichenfolge zu erstellen. Das Argument wird zur Laufzeit nicht ausgewertet und gilt als nicht erreichbarer Code (es wird jedoch keine Warnung "Nicht erreichbarer Code" ausgegeben).

Vom Namen des Betreibers - v5

Sie können dies anhand dieses TryRoslyn-Beispiels sehen :

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

Wird dazu kompiliert und dekompiliert:

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

Das Laufzeitäquivalent lautet:

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

Wie in den Kommentaren erwähnt, bedeutet dies, dass Sie bei der Verwendung nameofvon Typparametern in einem generischen Typ nicht erwarten, den Namen des tatsächlichen dynamischen Typs zu erhalten, der als Typparameter verwendet wird, sondern nur den Namen des Typparameters. Also das:

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

Wird dies werden:

public class Foo
{
    public void Bar<T>()
    {
        Console.WriteLine("T");
    }
}
i3arnon
quelle
Was ist hier "Kompilierungszeit"? Kompilierung nach MSIL oder Kompilierung nach nativem Code?
user541686
6
@Mehrdad Der C # -Compiler generiert IL.
i3arnon
3
Kurze Frage, kann ich nameof in einem Switch-Fall verwenden?
Zauber
2
@ Spell Ja
i3arnon
58

Ich wollte die Antwort von @ I3arnon mit einem Beweis bereichern, dass sie zur Kompilierungszeit ausgewertet wird.

Nehmen wir an, ich möchte den Namen einer Variablen in der Konsole mit dem nameofOperator drucken :

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

Wenn Sie die generierte MSIL auschecken, werden Sie feststellen, dass sie einer Zeichenfolgendeklaration entspricht, da ein Objektverweis auf eine Zeichenfolge mit dem ldstrOperator auf den Stapel verschoben wird:

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)

Sie werden feststellen, dass das Deklarieren der Vorname-Zeichenfolge und die Verwendung des nameofOperators denselben Code in MSIL generiert. Dies bedeutet, dass dies nameofgenauso effizient ist wie das Deklarieren einer Zeichenfolgenvariablen.

Faris Zacina
quelle
4
Wenn die MSIL in Quellcode dekompiliert wird, wie leicht kann der Dekompiler erkennen, dass es sich um einen nameofOperator und nicht um eine einfache fest codierte Zeichenfolge handelt?
ADTC
11
Das ist eine gute Frage! Sie können es als neue Frage auf SO posten, wenn Sie eine detaillierte Erklärung erhalten möchten :) .. Die kurze Antwort lautet jedoch, dass der Dekompiler nicht herausfinden kann, dass es sich um einen Namen des Operators handelt, sondern stattdessen ein Zeichenfolgenliteral verwendet . Ich habe überprüft, dass dies bei ILSpy und Reflector der Fall ist.
Faris Zacina
2
@ADTC: Da der Name von vollständig durch Laden eines Strings auf den Stapel ersetzt wird, wie könnte der Dekompiler überhaupt versuchen, zu erraten, dass es sich um einen Namen und nicht um einen einfachen konstanten Parameter handelt?
Quetzalcoatl
2
Das ist interessant. Möglicherweise könnte der Dekompiler die Zeichenfolge mit dem aktuellen Kontext vergleichen (Name der Methode / Eigenschaft / usw., in der Sie sich befinden). Trotzdem gibt es keine Möglichkeit, 100% zuverlässig zu sein - vielleicht haben Sie doch einen fest codierten String verwendet.
Gigi
2
Obwohl ich damit einverstanden bin, dass Sie nach dem Kompilieren nicht wissen können, ob es sich um einen Namen handelt, sehe ich noch keinen Hinweis darauf, dass ILSpy oder Reflector C # 6 unterstützen. Wenn das der Fall ist, können Sie es nicht testen @TheMinister
Millie Smith