Nameof () est-il évalué au moment de la compilation?

114

En C # 6, vous pouvez utiliser l' nameof()opérateur pour obtenir une chaîne contenant le nom d'une variable ou d'un type.

Est-ce évalué au moment de la compilation ou à l'exécution via une API Roslyn?

Gigi
la source
Roslyn est la nouvelle plateforme de compilation. Il n'est utilisé qu'au moment de la compilation.
Paulo Morgado
2
@PauloMorgado ce n'est pas vrai, vous pouvez utiliser Rosyln au moment de l'exécution pour faire des choses. Comme la construction d'un éditeur de code en direct ou l'utilisation de l'analyse syntaxique de Rosyln pour faire des choses avec des arbres ou des expressions ou quelque chose du
genre
@ChrisMarisic c'est mon impression, mais je n'ai pas répondu car mes connaissances sur le sujet sont limitées (d'où ma question). Je suis tombé sur ceci: scriptcs.net qui est un très bon exemple de la puissance de Roslyn, et qui, je crois, fait des choses d'exécution, mais je peux me tromper car je ne suis pas très bien informé à ce sujet.
Gigi
@ChrisMarisic, donc, ce que vous dites, c'est que vous pouvez utiliser Roslyn pour créer du code en direct à partir de la source, et non du seul binaire en cours d'exécution. Et vous utilisez toujours Roslyn pour transformer la source en binaires qui n'utiliseront pas Roslyn pour changer ces binaires. Si vous ne pouvez absolument pas utiliser Roslyn au moment de l'exécution, vous ne pourrez jamais compiler de code.
Paulo Morgado

Réponses:

119

Oui. nameof()est évalué au moment de la compilation. En regardant la dernière version des spécifications:

Le nom de l'expression est une constante. Dans tous les cas, nameof (...) est évalué au moment de la compilation pour produire une chaîne. Son argument n'est pas évalué à l'exécution, et est considéré comme du code inaccessible (cependant il n'émet pas d'avertissement "code inaccessible").

Depuis le nom de l'opérateur - v5

Vous pouvez le voir avec cet exemple TryRoslyn où ceci:

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

Est compilé et décompilé en ceci:

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

Son équivalent d'exécution est:

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

Comme mentionné dans les commentaires, cela signifie que lorsque vous utilisez des nameofparamètres de type dans un type générique, ne vous attendez pas à obtenir le nom du type dynamique réel utilisé comme paramètre de type au lieu du nom du paramètre de type uniquement. Donc ça:

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

Deviendra ceci:

public class Foo
{
    public void Bar<T>()
    {
        Console.WriteLine("T");
    }
}
i3arnon
la source
Qu'est-ce que "compile-time" ici? Compilation vers MSIL ou compilation vers du code natif?
user541686
6
@Mehrdad Le compilateur C # génère IL.
i3arnon
3
Question rapide, puis-je utiliser nameof dans un boîtier de commutation?
Épeler le
2
@Spell Yes
i3arnon
58

Je voulais enrichir la réponse fournie par @ I3arnon avec une preuve qu'elle est évaluée à la compilation.

Supposons que je souhaite imprimer le nom d'une variable dans la console à l'aide de l' nameofopérateur:

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

Lorsque vous extrayez le MSIL généré, vous verrez qu'il est équivalent à une déclaration de chaîne car une référence d'objet à une chaîne est poussée vers la pile à l'aide de l' ldstropérateur:

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)

Vous remarquerez que déclarer la chaîne de prénom et utiliser l' nameofopérateur génère le même code dans MSIL, ce qui signifie qu'il nameofest aussi efficace que de déclarer une variable de chaîne.

Faris Zacina
la source
4
Si le MSIL est décompilé en code source, dans quelle mesure sera-t-il facile pour le décompilateur de reconnaître qu'il s'agissait d'un nameofopérateur et non d'une chaîne codée en dur?
ADTC
11
C'est une bonne question! vous pouvez le poster comme une nouvelle question sur SO si vous voulez obtenir une explication détaillée :) .. cependant la réponse courte est que le décompilateur ne pourra pas comprendre que c'était un opérateur nameof, mais utilisera une chaîne littérale à la place . J'ai vérifié que c'était le cas avec ILSpy et Reflector.
Faris Zacina
2
@ADTC: Comme le nom de est entièrement remplacé par load-a-string-on-the-stack, comment le décompilateur pourrait-il même essayer de deviner que c'était un nom de, et pas un simple paramètre constant?
quetzalcoatl
2
C'est intéressant. Peut-être que le décompilateur pourrait vérifier la chaîne par rapport au contexte actuel (nom de la méthode / propriété / etc dans laquelle vous vous trouvez). Pourtant, il n'y a aucun moyen pour qu'il soit fiable à 100% - vous avez peut-être utilisé une chaîne codée en dur après tout.
Gigi
2
Bien que je convienne que vous ne pouvez pas savoir si c'est un nom après la compilation, je ne vois aucune indication que ILSpy ou Reflector prennent en charge C # 6 pour le moment. Si tel est le cas, vous ne pouvez pas le tester @TheMinister
Millie Smith