Wofür ist das Attribut __DynamicallyInvokable?

181

Beim Durchschauen System.Linq.Enumerablein DotPeek stelle ich fest, dass einige Methoden mit einem [__DynamicallyInvokable]Attribut versehen sind.

Welche Rolle spielt dieses Attribut? Wird es von DotPeek hinzugefügt oder spielt es eine andere Rolle und informiert den Compiler möglicherweise darüber, wie die Methoden am besten optimiert werden können?

Jamie Dixon
quelle
2
String.Empty hat dies übrigens auch.
Marc Gravell
1
Das tut es auch IReadOnlyCollection<T>.
Drew Noakes
1
Und System.ServiceModel v3's BasicHttpBinding.TextEncoding(das in V4 zu einer neuen Basisklasse aufgestiegen ist und wird HttpBindingBase.TextEncoding)
Ruben Bartelink
Es wird auch für die ganzzahligen Werte in System-Enums wie DayOfWeek
beauXjames
Sobald ich einen Fall habe, in dem eine Methode mit diesem Attribut in der generierten Assembly (DateTime.AddYears, .Net 4.5)
eingefügt wurde

Antworten:

139

Es ist nicht dokumentiert, sieht aber wie eine der Optimierungen in .NET 4.5 aus. Es scheint verwendet zu werden, um den Reflection-Typ-Info-Cache zu aktivieren, sodass nachfolgender Reflection-Code für gängige Framework-Typen schneller ausgeführt wird. Es gibt einen Kommentar dazu in der Referenzquelle für System.Reflection.Assembly.cs, RuntimeAssembly.Flags-Eigenschaft:

 // 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;
 }

Ohne weitere Hinweise, was eine "gesegnete API" bedeuten könnte. Obwohl aus dem Kontext klar hervorgeht, dass dies nur für Typen im Framework selbst funktioniert. Es sollte irgendwo zusätzlichen Code geben, der das Attribut überprüft, das auf Typen und Methoden angewendet wird. Keine Ahnung, wo sich das befindet, aber da es eine Ansicht aller .NET-Typen geben müsste, um das Caching zu testen, kann ich nur an Ngen.exe denken.

Hans Passant
quelle
7
Es sieht so aus, als würde der gespeicherte Wert verwendet, um zu überprüfen, ob die API in WP8 verfügbar ist.
usr
1
+1 Siehe meinen Kommentar zum Q des OP - ein Fall, in dem die CLR auf dieser Grundlage Tricks zu machen scheint, ist der Umgang mit 'leichten' Bewegungen von Methoden (z. B. eine Ebene nach unten auf eine neue Basisklasse) unter Vereinheitlichung
Ruben Bartelink
2
Das ist der Trick [TypeForwardTo], etwas völlig anderes.
Hans Passant
@HansPassant Interessant - hört sich so an, als ob ich mich irren könnte, also ... hatte ich nicht daran gedacht, die ursprüngliche Baugruppe / den ursprünglichen Typ zu untersuchen. Fazit ist, dass sich bei 4.5 die zitierte Eigenschaft (nicht der Typ) relativ zu der Position bei 3.5 (technisch System.ServiceModel 3.0) verschoben hat . Ich war davon ausgegangen, dass die Vereinheitlichung a la mscorlibReferenzen im Spiel ist, habe aber ohnehin noch einiges zu tun - werde mich zu gegebener Zeit bei meinen Kommentaren melden und / oder irreführenden Ton entfernen ...
Ruben Bartelink
1
@HansPassant Aus weiteren Recherchen ... Ich kann nichts über die Typweiterleitung sehen, die andere Dinge als die Weiterleitungstypen ausführt. Daher möchte ich mich an dieser Stelle von dem völlig anderen unterscheiden . Die Kräfte bei der Arbeit sind einfach, dass, wenn Sie eine CLR2-Assembly referenzieren System.ServiceModel v3, diese unter CLR4-Auto-Upgrades auf geladen wird System.ServiceModel v4. Das lustige daran ist, dass .NET 4.5 ein In-Place-Update für das System.ServiceModelAblegen einer neuen Basisklasse darunter durchführt und die Eigenschaft um eine Ebene nach unten verschiebt .
Ruben Bartelink
23

Ich fand, dass es in der Runtime*Info.IsNonW8PFrameworkAPI()Reihe der internen Methoden verwendet wird. Wenn dieses Attribut einem Mitglied zugewiesen wird, kehrt IsNonW8PFrameworkAPI () zurück falseund stellt das Mitglied in WinRT-Anwendungen zur Verfügung und schließt die The API '...' cannot be used on the current platform.Ausnahme.

Profiler-Autoren sollten dieses Attribut auf Mitglieder setzen, die von ihrem Profiler in Framework-Assemblys ausgegeben wurden, wenn sie unter WinRT darauf zugreifen möchten.

Stefan Dragnev
quelle
1
Ja, der von @Hans gefundene Code richtet die gesuchten Flags ein RuntimeAssembly.InvocableAttributeCtorToken, die von den von IsNonW8PFrameworkAPI()Ihnen erwähnten Methoden aufgerufen werden .
Mark Hurd