28 votes

C# interne statique externe avec l'attribut InternalCall - interne ou externe ?

Dans une autre question que j'ai posée, un commentaire a indiqué que la méthode Array.Copy du framework .NET utilise du code non managé. J'ai creusé avec Reflector et j'ai trouvé que la signature de l'une des surcharges de la méthode Array.Copy est définie ainsi :

[MethodImpl(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
internal static extern void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable);

Après avoir examiné cela, je suis légèrement confus. La source de ma confusion est le modificateur extern qui signifie (lien MSDN) :

Le modificateur extern est utilisé pour déclarer une méthode qui est implémentée de manière externe.

Cependant, la déclaration de la méthode est également décorée avec un attribut MethodImplOptions.InternalCall, qui indique (lien MSDN) :

Spécifie un appel interne. Un appel interne est un appel à une méthode qui est implémentée au sein du runtime commun du langage lui-même.

Quelqu'un peut-il expliquer cette contradiction apparemment évidente ?

37voto

280Z28 Points 49515

Je viens de commenter le post de leppie, mais il devenait un peu long.

Je travaille actuellement sur une implémentation expérimentale de l'interface en ligne de commande (CLI). Il existe de nombreux cas où une méthode (ou propriété) exposée publiquement ne peut être implémentée sans connaissance de la façon dont la machine virtuelle est implémentée en interne. Un exemple est OffsetToStringData, qui nécessite de savoir comment le gestionnaire de mémoire alloue les chaînes de caractères. p>

Pour des cas comme celui-ci, où il n'y a pas de code C# pour exprimer la méthode, chaque appel à la méthode peut être traité d'une manière spéciale interne au processus JIT. Par exemple, en remplaçant l'instruction call par un ldc.i4 (chargement d'un entier constant) avant de le passer au générateur de code natif. Le drapeau InternalCall signifie "Le corps de cette méthode est traité d'une manière spéciale par le runtime lui-même." Il peut y avoir ou non une implémentation réelle - dans plusieurs cas de mon code, l'appel est traité comme une fonction intrinsèque par le JIT.

Il existe d'autres cas où le JIT peut avoir des informations spéciales disponibles qui permettent une optimisation importante d'une méthode. Un exemple est les méthodes Math, où même si ces dernières peuvent être implémentées en C#, spécifier InternalCall pour les traiter effectivement comme des intrinsèques a des avantages significatifs en termes de performances.

En C#, une méthode doit avoir un corps à moins qu'elle ne soit abstract ou extern. Le extern signifie généralement "Vous pouvez appeler cette méthode à partir du code C#, mais son corps est en fait défini ailleurs.". Lorsque le JIT atteint un appel à une méthode extern, il recherche où trouver le corps et se comporte de différentes manières en fonction du résultat.

  • L'attribut DllImport indique au JIT de créer un stub P/Invoke pour appeler une implémentation en code natif.
  • Le drapeau InternalCall indique au JIT de traiter l'appel d'une manière définie par lui-même.
  • (Il y en a d'autres, mais je n'ai pas d'exemples en tête pour leur utilisation.)

14voto

leppie Points 67289

InternalCall signifie fourni par le framework.

extern indique que vous ne fournissez pas de code.

extern peut être utilisé dans 2 situations générales, comme ci-dessus, ou avec p/invoke.

Avec p/invoke, vous indiquez simplement à la méthode où obtenir l'implémentation.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X