¿Se evalúa nameof () en tiempo de compilación?

114

En C # 6, puede usar el nameof()operador para obtener una cadena que contenga el nombre de una variable o un tipo.

¿Esto se evalúa en tiempo de compilación o en tiempo de ejecución a través de alguna API de Roslyn?

Gigi
fuente
Roslyn es la nueva plataforma de compilación. Solo se usa en tiempo de compilación.
Paulo Morgado
2
@PauloMorgado eso no es cierto, puedes usar Rosyln en tiempo de ejecución para hacer cosas. Como construir un editor de código en vivo o usar el material de análisis de Rosyln para hacer cosas con árboles o expresiones o algo así
Chris Marisic
@ChrisMarisic esa es mi impresión, pero no respondí ya que mi conocimiento sobre el tema es limitado (de ahí mi pregunta). Me encontré con esto: scriptcs.net, que es un ejemplo bastante bueno del poder de Roslyn, y creo que hace cosas en tiempo de ejecución, pero podría estar equivocado ya que no estoy muy bien informado al respecto.
Gigi
@ChrisMarisic, entonces, lo que está diciendo es que puede usar Roslyn para construir código en vivo desde la fuente, no desde el único binario que se está ejecutando. Y todavía está usando Roslyn para transformar el código fuente en binarios que no usarán Roslyn para cambiar esos binries. Si no pudieras usar Roslyn completamente en tiempo de ejecución, nunca podrías compilar ningún código.
Paulo Morgado

Respuestas:

119

Si. nameof()se evalúa en tiempo de compilación. Mirando la última versión de las especificaciones:

El nombre de la expresión es una constante. En todos los casos, nameof (...) se evalúa en tiempo de compilación para producir una cadena. Su argumento no se evalúa en tiempo de ejecución y se considera código inalcanzable (sin embargo, no emite una advertencia de "código inalcanzable").

Desde el nombre del operador - v5

Puede ver que con este ejemplo de TryRoslyn donde esto:

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

Se compila y descompila en esto:

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

Su equivalente en tiempo de ejecución es:

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

Como se mencionó en los comentarios, eso significa que cuando use nameofparámetros de tipo en un tipo genérico, no espere obtener el nombre del tipo dinámico real que se usa como parámetro de tipo en lugar de solo el nombre del parámetro de tipo. Así que esto:

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

Se convertirá en esto:

public class Foo
{
    public void Bar<T>()
    {
        Console.WriteLine("T");
    }
}
i3arnon
fuente
¿Qué es "tiempo de compilación" aquí? ¿Compilación a MSIL o compilación a código nativo?
user541686
6
@Mehrdad El compilador de C # genera IL.
i3arnon
3
Pregunta rápida, ¿puedo usar nameof en una caja de interruptor?
Hechizo
2
@Spell Yes
i3arnon
58

Quería enriquecer la respuesta proporcionada por @ I3arnon con una prueba de que se evalúa en tiempo de compilación.

Supongamos que quiero imprimir el nombre de una variable en la Consola usando el nameofoperador:

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

Cuando revise el MSIL generado, verá que es equivalente a una declaración de cadena porque una referencia de objeto a una cadena se envía a la pila usando el ldstroperador:

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)

Notará que declarar la cadena de nombre y usar el nameofoperador genera el mismo código en MSIL, lo que significa que nameofes tan eficiente como declarar una variable de cadena.

Faris Zacina
fuente
4
Si el MSIL se descompila en código fuente, ¿qué tan fácil será para el descompilador reconocer que era un nameofoperador, no una cadena simple codificada?
ADTC
11
¡Buena pregunta! puede publicarlo como una nueva pregunta en SO si desea obtener una explicación detallada :) .. sin embargo, la respuesta corta es que el descompilador no podrá descubrir que era un operador de nombre de operador, pero usará una cadena literal en su lugar . He verificado que es el caso de ILSpy y Reflector.
Faris Zacina
2
@ADTC: Como el nombre de se reemplaza por completo con cargar-una-cadena-en-la-pila, ¿cómo podría el descompilador intentar adivinar que era un nombre de, y no un simple parámetro constante?
quetzalcoatl
2
Eso es interesante. Quizás el descompilador podría comparar la cadena con el contexto actual (nombre del método / propiedad / etc. en el que se encuentra). Aún así, no hay forma de que sea 100% confiable; después de todo, es posible que haya utilizado una cadena codificada.
Gigi
2
Si bien estoy de acuerdo en que no puede saber si es un nombre de después de compilar, no veo ninguna indicación de que ILSpy o Reflector sean compatibles con C # 6 todavía. Si ese es el caso, no puede probarlo @TheMinister
Millie Smith