Tout le code écrit en langage .NET se compile en MSIL, mais y a-t-il des tâches/opérations spécifiques que vous pouvez faire uniquement en utilisant MSIL directement?
Laissez-nous également faire les choses plus facilement dans MSIL que C #, VB.NET, F #, j # ou tout autre langage .NET.
Jusqu'à présent, nous avons ceci:
raise
.main()
comme .entrypoint
.int
et le natif unsigned int
saisit directement.protected internal
est fam ou assem)<Module>
classe pour définir les fonctions globales, ou un initialiseur de module.MSIL permet des surcharges qui ne diffèrent que par les types de retour en raison de
call void [mscorlib]System.Console::Write(string)
ou
callvirt int32 ...
La plupart des langages .Net, y compris C # et VB, n'utilisent pas la fonction de récursivité de queue du code MSIL.
La récursivité de la queue est une optimisation courante dans les langages fonctionnels. Cela se produit lorsqu'une méthode A se termine en renvoyant la valeur de la méthode B de telle sorte que la pile de la méthode A peut être désallouée une fois l'appel à la méthode B effectué.
Le code MSIL prend explicitement en charge la récursivité de queue, et pour certains algorithmes, cela pourrait être une optimisation importante à effectuer. Mais puisque C # et VB ne génèrent pas les instructions pour ce faire, cela doit être fait manuellement (ou en utilisant F # ou un autre langage).
Voici un exemple de la façon dont la récursivité de queue peut être implémentée manuellement en C #:
private static int RecursiveMethod(int myParameter)
{
// Body of recursive method
if (BaseCase(details))
return result;
// ...
return RecursiveMethod(modifiedParameter);
}
// Is transformed into:
private static int RecursiveMethod(int myParameter)
{
while (true)
{
// Body of recursive method
if (BaseCase(details))
return result;
// ...
myParameter = modifiedParameter;
}
}
Il est courant de supprimer la récursivité en déplaçant les données locales de la pile matérielle vers une structure de données de pile allouée en tas. Dans l'élimination de la récursivité des appels de queue comme indiqué ci-dessus, la pile est complètement éliminée, ce qui est une assez bonne optimisation. De plus, la valeur de retour ne doit pas remonter une longue chaîne d'appel, mais elle est retournée directement.
Mais, de toute façon, le CIL fournit cette fonctionnalité dans le cadre du langage, mais avec C # ou VB elle doit être implémentée manuellement. (La gigue est également libre de faire cette optimisation de son propre chef, mais c'est un tout autre problème.)
Dans MSIL, vous pouvez avoir une classe qui ne peut pas hériter de System.Object.
Exemple de code: compilez-le avec ilasm.exe PDATE: Vous devez utiliser "/ NOAUTOINHERIT" pour empêcher l'assembleur d'hériter automatiquement.
// Metadata version: v2.0.50215
.Assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 2:0:0:0
}
.Assembly sample
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module sample.exe
// MVID: {A224F460-A049-4A03-9E71-80A36DBBBCD3}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x02F20000
// =============== CLASS MEMBERS DECLARATION ===================
.class public auto ansi beforefieldinit Hello
{
.method public hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 13 (0xd)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Hello World!"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ret
} // end of method Hello::Main
} // end of class Hello
Il est possible de combiner les modificateurs d'accès protected
et internal
. En C #, si vous écrivez protected internal
un membre est accessible depuis l'assembly et depuis les classes dérivées. Via MSIL, vous pouvez obtenir un membre accessible à partir des classes dérivées de l'assembly niquement. (Je pense que cela pourrait être très utile!)
Ooh, je n'ai pas remarqué ça à l'époque. (Si vous ajoutez la balise jon-skeet, il est plus probable, mais je ne la vérifie pas souvent.)
Il semble que vous ayez déjà de très bonnes réponses. En outre:
object
en C #, cela fonctionnera parfois. Voir un int []/int [] SO question pour un exemple.J'y ajouterai si je pense à autre chose ...
Le CLR prend déjà en charge la co/contravariance générique, mais C # n'obtiendra pas cette fonctionnalité avant 4.0
Dans IL, vous pouvez lancer et intercepter n'importe quel type, pas seulement les types dérivés de System.Exception
.
IL fait la distinction entre call
et callvirt
pour les appels de méthode virtuelle. En utilisant la première, vous pouvez forcer l'appel d'une méthode virtuelle de type de classe statique actuel au lieu de la fonction virtuelle dans le type de classe dynamique.
C # n'a aucun moyen de le faire:
abstract class Foo {
public void F() {
Console.WriteLine(ToString()); // Always a virtual call!
}
public override string ToString() { System.Diagnostics.Debug.Assert(false); }
};
sealed class Bar : Foo {
public override string ToString() { return "I'm called!"; }
}
VB, comme IL, peut émettre des appels non virtuels en utilisant la syntaxe MyClass.Method()
. Dans ce qui précède, ce serait MyClass.ToString()
.
Dans un try/catch, vous pouvez entrer à nouveau le bloc try à partir de son propre bloc catch. Donc, vous pouvez le faire:
.try {
// ...
MidTry:
// ...
leave.s RestOfMethod
}
catch [mscorlib]System.Exception {
leave.s MidTry // branching back into try block!
}
RestOfMethod:
// ...
AFAIK vous ne pouvez pas faire cela en C # ou VB
Avec IL et VB.NET, vous pouvez ajouter des filtres lors de la capture d'exceptions, mais C # v3 ne prend pas en charge cette fonctionnalité.
Cet exemple VB.NET est tiré de http://blogs.msdn.com/clrteam/archive/2009/02/05/catch-rethrow-and-filters-why-you-should-care.aspx (notez la When ShouldCatch(ex) = True
dans la clause Catch):
Try
Foo()
Catch ex As CustomBaseException When ShouldCatch(ex)
Console.WriteLine("Caught exception!")
End Try
Pour autant que je sache, il n'y a aucun moyen de faire des initialiseurs de module (constructeurs statiques pour un module entier) directement en C #:
http://blogs.msdn.com/junfeng/archive/2005/11/19/494914.aspx
Native types
Vous pouvez travailler directement avec les types int natifs et int natifs non signés (en c #, vous ne pouvez travailler que sur un IntPtr qui n'est pas le même.
Transient Pointers
Vous pouvez jouer avec des pointeurs transitoires, qui sont des pointeurs vers des types gérés mais garantis de ne pas se déplacer dans la mémoire car ils ne sont pas dans le tas géré. Je ne suis pas tout à fait sûr de la façon dont vous pourriez utiliser cela sans jouer avec du code non managé, mais il n'est pas exposé directement aux autres langages uniquement via des choses comme stackalloc.
<Module>
vous pouvez jouer avec la classe si vous le désirez (vous pouvez le faire par réflexion sans avoir besoin d'IL)
.emitbyte
15.4.1.1 La directive .emitbyte MethodBodyItem :: =… | .emitbyte Int32 Cette directive provoque l'émission directe d'une valeur 8 bits non signée dans le flux CIL de la méthode, au point où la directive apparaît. [Remarque: La directive .emitbyte est utilisée pour générer des tests. Il n'est pas nécessaire pour générer des programmes réguliers. note de fin]
.entrypoint
Vous avez un peu plus de flexibilité à ce sujet, vous pouvez l'appliquer à des méthodes non appelées Main par exemple.
lire la spec Je suis sûr que vous en trouverez quelques autres.
En voici encore plus:
Je pense que celui que je continuais à souhaiter (avec des raisons entièrement erronées) était l'héritage dans Enums. Cela ne semble pas être une chose difficile à faire dans SMIL (car les Enums ne sont que des classes) mais ce n'est pas quelque chose que la syntaxe C # veut que vous fassiez.
Quelque chose que les obscurcisseurs utilisent - vous pouvez avoir un champ/méthode/propriété/événement tous portant le même nom.
20) Vous pouvez traiter un tableau d'octets comme un tableau (4x plus petit) d'entiers.
J'ai récemment utilisé cela pour faire une implémentation rapide XOR, car la fonction CLR xor fonctionne sur les entiers et je devais faire XOR sur un flux d'octets).
Le code résultant mesuré pour être ~ 10x plus rapide que l'équivalent fait en C # (faisant XOR sur chaque octet).
===
Je n'ai pas assez de credz de rue stackoverflow pour éditer la question et l'ajouter à la liste en tant que # 20, si quelqu'un d'autre pouvait le faire gonfler ;-)
L'héritage enum n'est pas vraiment possible:
Vous pouvez hériter d'une classe Enum. Mais le résultat ne se comporte pas comme un Enum en particulier. Il ne se comporte même pas comme un type de valeur, mais comme une classe ordinaire. La chose étrange est: IsEnum: True, IsValueType: True, IsClass: False
Mais ce n'est pas particulièrement utile (sauf si vous voulez confondre une personne ou le runtime lui-même.)
Vous pouvez également dériver une classe du délégué System.Multicast en IL, mais vous ne pouvez pas le faire en C #:
// La définition de classe suivante est illégale:
classe publique YourCustomDelegate: MulticastDelegate {}
Vous pouvez également définir des méthodes au niveau du module (alias global) en IL, et C #, en revanche, vous permet uniquement de définir des méthodes tant qu'elles sont attachées à au moins un type.