Por que o compilador C # emitindo uma instrução callvirt para uma chamada de método GetType ()?

StackOverflow https://stackoverflow.com/questions/845657

Pergunta

Estou curioso para saber por que isso está acontecendo. Por favor, leia o exemplo de código abaixo e a IL correspondente que foi emitida nos comentários abaixo de cada seção:

using System;

class Program
{
    static void Main()
    {
        Object o = new Object();
        o.GetType();

        // L_0001: newobj instance void [mscorlib]System.Object::.ctor()
        // L_0006: stloc.0 
        // L_0007: ldloc.0 
        // L_0008: callvirt instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()

        new Object().GetType();

        // L_000e: newobj instance void [mscorlib]System.Object::.ctor()
        // L_0013: call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
    }
}

Por que o compilador emitir um callvirt para a primeira seção, mas um call para a segunda seção? Existe alguma razão para que o compilador jamais iria emitir uma instrução callvirt por um método não-virtual? E se há casos em que o compilador irá emitir um callvirt para um método não-virtual que isso crie problemas para o Tipo-segurança?

Foi útil?

Solução

Apenas jogar pelo seguro.

Tecnicamente C # compilador não sempre uso callvirt

Para métodos estáticos e métodos definidos em tipos de valor, ele usa call. A maioria é fornecido através da instrução callvirt IL.

A diferença que balançou o voto entre os dois é o fato de que call assume o "objeto que está sendo usado para fazer a chamada" não é nulo. callvirt nas outras verificações mão para não nulo e lança um NullReferenceException, se necessário.

  • Para métodos estáticos, o objeto é um objeto do tipo e não pode ser nulo. Ditto para tipos de valor. Daí call é usado para eles -. Melhor desempenho
  • Para os outros, os projetistas da linguagem decidiu ir com callvirt para que os verifica compilador JIT que o objeto que está sendo usado para fazer a chamada não é nulo. Mesmo para métodos de instância não-virtuais .. eles valorizado segurança sobre o desempenho.

Veja também: Jeff Richter faz um trabalho melhor neste - no capítulo seu 'Designing Tipos' em CLR via C # 2ª Ed

Outras dicas

este velho post por Eric Gunnerson.

Aqui está o texto da mensagem:

Por que C # sempre usar callvirt?

Esta questão surgiu em um C # apelido interno, e eu pensei que a resposta seria de interesse geral. Isso supondo que a resposta está correta - tem sido um bom tempo

.

A linguagem .NET IL oferece tanto uma chamada e instrução callvirt, com o callvirt sendo usado para chamar funções virtuais. Mas se você olhar através do código que C # gera, você vai ver que ele gera um "callvirt" mesmo nos casos em que não há nenhuma função virtual envolvidos. Por que fazer isso?

Voltei através das notas de design de linguagem que eu tenho, e eles afirmam claramente que nós decidimos usar callvirt em 1999/12/13. Infelizmente, eles não captam a nossa razão para fazer isso, então eu vou ter que ir da minha memória.

Nós tínhamos chegado um relatório de alguém (um provável dos grupos .NET usando C # (pensei que ainda não foi nomeado C # naquele tempo)) que tinha escrito código que chamou um método em um ponteiro nulo, mas eles didn 't obter uma exceção porque o método não acessar quaisquer campos (ou seja, ‘este’ foi nula, mas nada no método utilizado). Esse método chamado então um outro método que usou a este ponto e lançou uma exceção, e um pouco de cabeça coçar seguiu. Depois que percebi isso, enviaram-nos uma nota sobre o assunto.

Nós pensamos que ser capaz de chamar um método em uma instância nula foi um pouco estranho. Peter Golde fiz alguns testes para ver o que o impacto perf foi sempre de usar callvirt, e foi pequena o suficiente para que nós decidimos fazer a mudança.

Como (talvez-) aparte interessante ... GetType() é incomum em que não virtual - Isso leva a algumas coisas muito, muito estranhas .

(marcado como wiki, uma vez que é um pouco off-topic para a questão real)

O compilador não sabe o tipo real do o na primeira expressão, mas não sabe o tipo real no segundo expressão. Parece que ele só está olhando para uma instrução de cada vez.

Isso é bom, porque C # depende muito do JIT para otimização. É muito provável que em um caso tão simples que ambas as chamadas se tornará chamadas de instância em tempo de execução.

Eu não acredito callvirt nunca é emitida para métodos não-virtuais, mas mesmo que fosse, não seria nenhum problema porque o método nunca seria substituído (por razões óbvias).

Eu arriscaria um palpite de que é porque os primeiros atribui a uma variável, o que potencialmente poderia conter uma instância downcasted de outro tipo que poderia ter GetType substituído (embora nós podemos ver que não faz); o segundo nunca poderia ser outra coisa senão Object.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top