Todo o código escrito nas linguagens .NET é compilado para o MSIL, mas existem tarefas / operações específicas que você só pode fazer usando o MSIL diretamente?
Vamos também fazer coisas mais fáceis no MSIL do que C #, VB.NET, F #, j # ou qualquer outra linguagem .NET.
Até agora, temos o seguinte:
- Recursão da cauda
- Co / Contravariância Genérica
- Sobrecargas que diferem apenas nos tipos de retorno
- Substituir modificadores de acesso
- Tem uma classe que não pode herdar do System.Object
- Exceções filtradas (podem ser feitas em vb.net)
- Chamando um método virtual do tipo de classe estática atual.
- Identifique a versão em caixa de um tipo de valor.
- Faça uma tentativa / falha.
- Uso de nomes proibidos.
- Defina seus próprios construtores sem parâmetros para tipos de valor .
- Defina eventos com um
raise
elemento. - Algumas conversões permitidas pelo CLR, mas não pelo C #.
- Faça um
main()
método não como o.entrypoint
. - trabalhe diretamente com os tipos
int
nativo e nativounsigned int
. - Brincar com ponteiros transitórios
- diretiva emitbyte em MethodBodyItem
- Lançar e capturar tipos não System.Exception
- Herdar enums (não verificado)
- Você pode tratar uma matriz de bytes como uma matriz de ints (4x menor).
- Você pode ter um campo / método / propriedade / evento com o mesmo nome (Não verificado).
- Você pode ramificar novamente em um bloco try a partir de seu próprio bloco catch.
- Você tem acesso ao especificador de acesso famandassem (
protected internal
é fam ou assem) - Acesso direto à
<Module>
classe para definir funções globais ou um inicializador de módulo.
Respostas:
O MSIL permite sobrecargas que diferem apenas nos tipos de retorno devido a
ou
fonte
A maioria dos idiomas .Net, incluindo C # e VB, não usa o recurso de recursão de cauda do código MSIL.
A recursão da cauda é uma otimização comum em linguagens funcionais. Ocorre quando um método A termina retornando o valor do método B, para que a pilha do método A possa ser desalocada depois que a chamada para o método B é feita.
O código MSIL suporta explicitamente a recursão da cauda e, para alguns algoritmos, isso pode ser uma otimização importante a ser feita. Mas como C # e VB não geram as instruções para fazer isso, isso deve ser feito manualmente (ou usando F # ou algum outro idioma).
Aqui está um exemplo de como a recursão de cauda pode ser implementada manualmente em C #:
É prática comum remover a recursão movendo os dados locais da pilha de hardware para uma estrutura de dados da pilha alocada em heap. Na eliminação da recursão de chamada final, como mostrado acima, a pilha é eliminada completamente, o que é uma otimização bastante boa. Além disso, o valor de retorno não precisa subir uma longa cadeia de chamadas, mas é retornado diretamente.
Mas, de qualquer maneira, o CIL fornece esse recurso como parte do idioma, mas com C # ou VB ele precisa ser implementado manualmente. (O tremor também é livre para fazer essa otimização por conta própria, mas isso é outra questão.)
fonte
No MSIL, você pode ter uma classe que não pode herdar do System.Object.
Código de exemplo: compile-o com ilasm.exe ATUALIZAÇÃO: Você deve usar "/ NOAUTOINHERIT" para impedir a montagem automática da herança.
fonte
TypeLoadException
). PEVerify retorna: [MD]: Erro: TypeDef que não é uma Interface e não a classe Object estende o token Nil.É possível combinar os modificadores
protected
einternal
acesso. Em C #, se você escrever,protected internal
um membro poderá ser acessado a partir do assembly e de classes derivadas. Via MSIL você pode obter um membro que é acessível a partir de classes derivadas dentro da montagem única . (Eu acho que poderia ser bastante útil!)fonte
private protected
Ooh, eu não vi isso na época. (Se você adicionar a tag jon-skeet, é mais provável, mas eu não a checo com tanta frequência.)
Parece que você já tem respostas muito boas. Além do que, além do mais:
object
C #, isso às vezes funcionará. Veja uma pergunta SO uint [] / int [] SO para obter um exemplo.Vou acrescentar a isso se pensar em mais alguma coisa ...
fonte
<>a
como um nome em C # ...O CLR já suporta co / contravariância genérica, mas o C # não está recebendo esse recurso até 4.0
fonte
Em IL, você pode lançar e capturar qualquer tipo, não apenas os tipos derivados
System.Exception
.fonte
try
/catch
sem parênteses na instrução catch. Também capturará exceções que não são do tipo exceção. Jogar, no entanto, só é possível quando você herdaException
.IL tem a distinção entre
call
ecallvirt
para chamadas de método virtual. Usando o primeiro, você pode forçar a chamada de um método virtual do tipo de classe estática atual em vez da função virtual no tipo de classe dinâmica.C # não tem como fazer isso:
O VB, como o IL, pode emitir chamadas não virtuais usando a
MyClass.Method()
sintaxe. No exposto, isso seriaMyClass.ToString()
.fonte
Em uma tentativa / captura, você pode inserir novamente o bloco try a partir do seu próprio bloco catch. Então, você pode fazer isso:
AFAIK, você não pode fazer isso em C # ou VB
fonte
GOTO
Catch
para aTry
. Execute o código de teste online aqui .Com o IL e o VB.NET, você pode adicionar filtros ao capturar exceções, mas o C # v3 não suporta esse recurso.
Este exemplo do VB.NET é obtido em http://blogs.msdn.com/clrteam/archive/2009/02/05/catch-rethrow-and-filters-why-you-should-care.aspx (observe a
When ShouldCatch(ex) = True
seção Cláusula de captura):fonte
= True
, está fazendo meus olhos sangrarem!Até onde eu sei, não há como criar inicializadores de módulo (construtores estáticos para um módulo inteiro) diretamente em C #:
http://blogs.msdn.com/junfeng/archive/2005/11/19/494914.aspx
fonte
Native types
Você pode trabalhar diretamente com os tipos int nativo e int não assinado nativo (em c #, você só pode trabalhar em um IntPtr que não seja o mesmo.
Transient Pointers
Você pode jogar com ponteiros transitórios, que são ponteiros para tipos gerenciados, mas garantidos para não serem movidos na memória, pois não estão no heap gerenciado. Não tem muita certeza de como você poderia usá-lo de maneira útil sem mexer com código não gerenciado, mas ele não é exposto a outros idiomas diretamente apenas através de coisas como stackalloc.
<Module>
você pode mexer com a classe se assim o desejar (você pode fazer isso refletindo sem precisar de IL)
.emitbyte
.entrypoint
Você tem um pouco mais de flexibilidade nisso, pode aplicá-lo a métodos não chamados Main, por exemplo.
leia as especificações . Tenho certeza que você encontrará mais algumas.
fonte
<Module>
significa uma classe especial para idiomas que aceitam métodos globais (como o VB), mas, na verdade, o C # não pode acessá-lo diretamente.Você pode hackear a substituição de método co / contra-variação, que o C # não permite (NÃO é o mesmo que variação genérica!). Eu tenho mais informações sobre como implementar isso aqui , e as partes 1 e 2
fonte
Eu acho que o que eu continuava desejando (com razões inteiramente erradas) era herança na Enums. Não parece uma coisa difícil de fazer no SMIL (já que Enums são apenas classes), mas não é algo que a sintaxe do C # queira que você faça.
fonte
Aqui está um pouco mais:
fonte
20) Você pode tratar uma matriz de bytes como uma matriz (4x menor) de entradas.
Eu usei isso recentemente para fazer uma implementação rápida do XOR, já que a função CLR xor opera em ints e eu precisava fazer o XOR em um fluxo de bytes.
O código resultante medido para ser ~ 10x mais rápido que o equivalente feito em C # (executando XOR em cada byte).
===
Eu não tenho credibilidade de rua de stackoverflow suficiente para editar a pergunta e adicioná-la à lista como # 20, se alguém puder, isso seria inchado ;-)
fonte
Algo que os ofuscadores usam - você pode ter um campo / método / propriedade / evento, todos com o mesmo nome.
fonte
A herança de enum não é realmente possível:
Você pode herdar de uma classe Enum. Mas o resultado não se comporta como um Enum em particular. Ele se comporta nem mesmo como um tipo de valor, mas como uma classe comum. A coisa mais interessante é: IsEnum: True, IsValueType: True, IsClass: False
Mas isso não é particularmente útil (a menos que você queira confundir uma pessoa ou o próprio tempo de execução.)
fonte
Você também pode derivar uma classe do delegado System.Multicast em IL, mas não pode fazer isso em C #:
fonte
Você também pode definir métodos em nível de módulo (também conhecidos como globais) em IL, e o C #, por outro lado, permite definir métodos apenas desde que estejam anexados a pelo menos um tipo.
fonte