À quoi sert l'attribut __DynamicallyInvokable?

181

En regardant System.Linq.Enumerabledans DotPeek, je remarque que certaines méthodes sont parfumées avec un [__DynamicallyInvokable]attribut.

Quel rôle joue cet attribut? Est-ce quelque chose d'ajouté par DotPeek ou joue-t-il un autre rôle, en informant peut-être le compilateur sur la meilleure façon d'optimiser les méthodes?

Jamie Dixon
la source
2
String.Empty a également ceci, btw.
Marc Gravell
1
Il en va de même IReadOnlyCollection<T>.
Drew Noakes
1
And System.ServiceModel v3's BasicHttpBinding.TextEncoding(qui dans V4 est passé à une nouvelle classe de base et devient HttpBindingBase.TextEncoding)
Ruben Bartelink
il est également utilisé pour les valeurs entières dans les énumérations système comme DayOfWeek
beauXjames
une fois que j'ai un cas où la méthode avec cet attribut a été intégrée dans l'assemblage généré (DateTime.AddYears, .Net 4.5)
gdbdable

Réponses:

139

Il n'est pas documenté, mais il ressemble à l'une des optimisations de .NET 4.5. Il semble être utilisé pour amorcer le cache d'informations de type de réflexion, ce qui accélère le code de réflexion ultérieur sur les types de framework courants. Il y a un commentaire à ce sujet dans la source de référence pour la propriété System.Reflection.Assembly.cs, RuntimeAssembly.Flags:

 // Each blessed API will be annotated with a "__DynamicallyInvokableAttribute".
 // This "__DynamicallyInvokableAttribute" is a type defined in its own assembly.
 // So the ctor is always a MethodDef and the type a TypeDef.
 // We cache this ctor MethodDef token for faster custom attribute lookup.
 // If this attribute type doesn't exist in the assembly, it means the assembly
 // doesn't contain any blessed APIs.
 Type invocableAttribute = GetType("__DynamicallyInvokableAttribute", false);
 if (invocableAttribute != null)
 {
     Contract.Assert(((MetadataToken)invocableAttribute.MetadataToken).IsTypeDef);

     ConstructorInfo ctor = invocableAttribute.GetConstructor(Type.EmptyTypes);
     Contract.Assert(ctor != null);

     int token = ctor.MetadataToken;
     Contract.Assert(((MetadataToken)token).IsMethodDef);

     flags |= (ASSEMBLY_FLAGS)token & ASSEMBLY_FLAGS.ASSEMBLY_FLAGS_TOKEN_MASK;
 }

Sans plus d'indications sur ce que pourrait signifier une "API bénie". Bien qu'il soit clair d'après le contexte que cela ne fonctionnera que sur les types du framework lui-même. Il devrait y avoir du code supplémentaire quelque part qui vérifie l'attribut appliqué aux types et aux méthodes. Aucune idée de l'endroit où cela se trouve, mais étant donné qu'il faudrait avoir une vue de tous les types .NET pour avoir une chance de mettre en cache, je ne peux penser qu'à Ngen.exe.

Hans Passant
la source
7
Il semble que la valeur stockée soit utilisée pour vérifier si l'API est disponible sur WP8.
usr
1
+1 Voir mon commentaire sur le Q de l'OP - un cas où le CLR semble faire de la supercherie sur cette base est dans la gestion de `` légers '' mouvements de méthodes (par exemple vers le bas d'un niveau sur une nouvelle classe de base) sous l'unification
Ruben Bartelink
2
C'est l'astuce [TypeForwardTo], quelque chose de complètement différent.
Hans Passant
@HansPassant Intéressant - on dirait que je me trompe peut-être, donc ... je n'avais pas pensé à examiner l'assemblage / le type d'origine. En fin de compte, sur 4.5, la propriété citée (pas le type) a déménagé par rapport à l'endroit où elle se trouvait sur 3.5 (techniquement, System.ServiceModel 3.0). J'avais supposé que l'unification à la des mscorlibréférences était en jeu, mais j'ai quand même beaucoup de choses à faire sur mon problème spécifique - je ferai rapport et / ou supprimerai tout ton trompeur à mes commentaires en temps voulu ...
Ruben Bartelink
1
@HansPassant Des recherches plus approfondies ... Je ne vois rien concernant le transfert de type faisant des choses autres que des types de transfert, donc à ce stade, je vous prie de ne pas être d'accord avec le quelque chose de complètement différent . Les forces à l'œuvre sont simplement que lorsque vous avez un assemblage CLR2 référencé System.ServiceModel v3, le chargement sous CLR4 se met automatiquement à niveau vers System.ServiceModel v4. Le plus amusant est que .NET 4.5 effectue une mise à jour sur place des bits de System.ServiceModelchute dans une nouvelle classe de base en dessous et déplace la propriété d'un niveau vers le bas .
Ruben Bartelink le
23

J'ai trouvé qu'il était utilisé dans la Runtime*Info.IsNonW8PFrameworkAPI()suite de méthodes internes. Le fait de placer cet attribut sur un membre fait que IsNonW8PFrameworkAPI () le renvoie falseet rend ainsi le membre disponible dans les applications WinRT et ferme l' The API '...' cannot be used on the current platform.exception.

Les rédacteurs du profileur doivent placer cet attribut sur les membres émis par leur profileur dans des assemblys de structure, s'ils souhaitent y accéder sous WinRT.

Stefan Dragnev
la source
1
Ouais, le code trouvé par @Hans configure les indicateurs recherchés par RuntimeAssembly.InvocableAttributeCtorToken, qui sont appelés par les IsNonW8PFrameworkAPI()méthodes que vous mentionnez.
Mark Hurd