web-dev-qa-db-fra.com

Que pouvez-vous faire dans MSIL que vous ne pouvez pas faire en C # ou VB.NET?

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:

  1. Récursivité de la queue
  2. Co/Contravariance générique
  3. Surcharges qui ne diffèrent que par les types de retour
  4. Remplacer les modificateurs d'accès
  5. Avoir une classe qui ne peut pas hériter de System.Object
  6. Exceptions filtrées (peuvent être effectuées sur vb.net)
  7. Appel d'une méthode virtuelle du type de classe statique actuel.
  8. Obtenez une poignée sur la version encadrée d'un type de valeur.
  9. Faites un essai/une faute.
  10. Utilisation de noms interdits.
  11. Définissez vos propres constructeurs sans paramètre pour les types de valeur .
  12. Définissez les événements avec un élément raise.
  13. Certaines conversions autorisées par le CLR mais pas par C #.
  14. Créez une méthode non main() comme .entrypoint.
  15. travailler avec le natif int et le natif unsigned int saisit directement.
  16. Jouez avec des pointeurs transitoires
  17. directive emitbyte dans MethodBodyItem
  18. Lancer et intercepter les types non System.Exception
  19. Hériter des énumérations (non vérifiées)
  20. Vous pouvez traiter un tableau d'octets comme un tableau (4x plus petit) d'entiers.
  21. Vous pouvez avoir un champ/méthode/propriété/événement tous portant le même nom (non vérifié).
  22. Vous pouvez vous ramifier dans un bloc try à partir de son propre bloc catch.
  23. Vous avez accès au spécificateur d'accès famandassem (protected internal est fam ou assem)
  24. Accès direct au <Module> classe pour définir les fonctions globales, ou un initialiseur de module.
164
Binoj Antony

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 ...
34
Anton Gogolev

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.)

29
Jeffrey L Whitledge

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
21
Ramesh

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!)

20
yatima2975

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:

  • Vous ne pouvez pas obtenir une poignée sur la version encadrée d'un type de valeur en C #. Vous pouvez en C++/CLI
  • Vous ne pouvez pas faire un essai/faute en C # ("faute" est comme un "attraper tout et relancer à la fin du bloc" ou "finalement mais seulement en cas d'échec")
  • Il y a beaucoup de noms qui sont interdits par C # mais IL légal
  • IL vous permet de définir vos propres constructeurs sans paramètres pour les types de valeur .
  • Vous ne pouvez pas définir d'événements avec un élément "raise" en C #. (Dans VB vous vous avez pour les événements personnalisés, mais les événements "par défaut" n'en incluent pas.)
  • Certaines conversions sont autorisées par le CLR mais pas par C #. Si vous passez par object en C #, cela fonctionnera parfois. Voir un int []/int [] SO question pour un exemple.

J'y ajouterai si je pense à autre chose ...

18
Jon Skeet

Le CLR prend déjà en charge la co/contravariance générique, mais C # n'obtiendra pas cette fonctionnalité avant 4.0

17
ermau

Dans IL, vous pouvez lancer et intercepter n'importe quel type, pas seulement les types dérivés de System.Exception.

14
Daniel Earwicker

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().

10
Konrad Rudolph

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

9
thecoop

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
9
Emanuele Aina

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

8
yoyoyoyosef

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.

7
ShuggyCoUk

Vous pouvez pirater la méthode pour remplacer la co/contre-variance, ce que C # ne permet pas (ce n'est pas la même chose que la variance générique!). J'ai plus d'informations sur la mise en œuvre de cette ici , et les pièces 1 et 2

6
thecoop

En voici encore plus:

  1. Vous pouvez avoir des méthodes d'instance supplémentaires dans les délégués.
  2. Les délégués peuvent implémenter des interfaces.
  3. Vous pouvez avoir des membres statiques dans les délégués et les interfaces.
4
leppie

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.

4
Shalom Craimer

Quelque chose que les obscurcisseurs utilisent - vous pouvez avoir un champ/méthode/propriété/événement tous portant le même nom.

3
Jason Haley

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 ;-)

3
rosenfield

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.)

2
Zotta

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 {}

2
plaureano

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.

1
plaureano