Nameof () é avaliado em tempo de compilação?

114

No C # 6, você pode usar o nameof()operador para obter uma string contendo o nome de uma variável ou tipo.

Isso é avaliado em tempo de compilação ou em tempo de execução por meio de alguma API Roslyn?

Gigi
fonte
Roslyn é a nova plataforma de compilador. Só é usado em tempo de compilação.
Paulo Morgado
2
@PauloMorgado isso não é verdade, você pode usar Rosyln em tempo de execução para fazer as coisas. Como construir um editor de código ativo ou usar a análise de Rosyln para fazer coisas com árvores ou expressões ou algo assim
Chris Marisic,
@ChrisMarisic essa é a minha impressão, mas não respondi pois meu conhecimento sobre o assunto é limitado (daí minha pergunta). Eu me deparei com isso: scriptcs.net que é um bom exemplo do poder de Roslyn, e que acredito que faça coisas de tempo de execução, mas posso estar errado, pois não estou muito bem informado sobre isso.
Gigi
@ChrisMarisic, então, o que você está dizendo é que você pode usar o Roslyn para construir código ativo a partir do código-fonte, não a partir de um binário em execução. E você ainda está usando o Roslyn para transformar a origem em binários que não usará o Roslyn para alterar esses binários. Se você não pudesse usar o Roslyn de forma absoluta em tempo de execução, nunca poderia compilar nenhum código.
Paulo Morgado

Respostas:

119

Sim. nameof()é avaliado em tempo de compilação. Olhando para a versão mais recente das especificações:

O nome da expressão é uma constante. Em todos os casos, nameof (...) é avaliado em tempo de compilação para produzir uma string. Seu argumento não é avaliado em tempo de execução e é considerado código inacessível (entretanto, não emite um aviso de "código inacessível").

De nameof operator - v5

Você pode ver isso com este exemplo TryRoslyn onde isto:

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

É compilado e descompilado em:

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

Seu equivalente em tempo de execução é:

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

Como foi mencionado nos comentários, isso significa que quando você usa nameofem parâmetros de tipo em um tipo genérico, não espere obter o nome do tipo dinâmico real usado como um parâmetro de tipo em vez de apenas o nome do parâmetro de tipo. Então, é isso:

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

Vai se tornar isso:

public class Foo
{
    public void Bar<T>()
    {
        Console.WriteLine("T");
    }
}
i3arnon
fonte
O que é "tempo de compilação" aqui? Compilação para MSIL ou compilação para código nativo?
user541686
6
@Mehrdad O compilador C # gera IL.
i3arnon
3
Pergunta rápida, posso usar nameof em um caso de switch?
Soletrar
2
@Spell Sim
i3arnon
58

Eu queria enriquecer a resposta fornecida por @ I3arnon com uma prova de que ela é avaliada em tempo de compilação.

Vamos supor que eu queira imprimir o nome de uma variável no console usando o nameofoperador:

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

Ao verificar o MSIL gerado, você verá que ele é equivalente a uma declaração de string porque uma referência de objeto a uma string é enviada para a pilha usando o 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)

Você notará que declarar a string de nome e usar o nameofoperador gera o mesmo código em MSIL, o que significa que nameofé tão eficiente quanto declarar uma variável de string.

Faris Zacina
fonte
4
Se o MSIL for descompilado para o código-fonte, quão fácil será para o descompilador reconhecer que era um nameofoperador, não uma string codificada permanentemente?
ADTC
11
Esta é uma boa pergunta! você pode postá-lo como uma nova pergunta no SO se quiser obter uma explicação detalhada :) .. no entanto, a resposta curta é que o descompilador não será capaz de descobrir que era um operador nameof, mas usará um literal de string em vez . Verifiquei que é o caso com ILSpy e Reflector.
Faris Zacina
2
@ADTC: Como o nameof foi totalmente substituído por load-a-string-on-the-stack, como o descompilador poderia tentar adivinhar que era um nameof e não um parâmetro constante simples?
quetzalcoatl
2
Isso é interessante. Talvez o descompilador possa verificar a string em relação ao contexto atual (nome do método / propriedade / etc em que você está). Ainda assim, não há como ser 100% confiável - você pode ter usado uma string codificada no final das contas.
Gigi
2
Embora eu concorde que você não pode saber se é um nameof após a compilação, não vejo qualquer indicação de que ILSpy ou Reflector suportam C # 6 ainda. Se for esse o caso, você não pode testá-lo @TheMinister
Millie Smith