A cosa serve l'attributo __DynamicallyInvokable?


181

Guardando System.Linq.Enumerablein DotPeek noto che alcuni metodi sono aromatizzati con un [__DynamicallyInvokable]attributo.

Che ruolo gioca questo attributo? È qualcosa aggiunto da DotPeek o svolge un altro ruolo, forse informando il compilatore su come ottimizzare al meglio i metodi?


2
String.Empty ha anche questo, tra l'altro.
Marc Gravell

1
Così fa IReadOnlyCollection<T>.
Drew Noakes,

1
E System.ServiceModel v3' BasicHttpBinding.TextEncoding(che nel V4 è passato a una nuova classe base e diventa HttpBindingBase.TextEncoding)
Ruben Bartelink,

è anche usato per i valori interi negli enum di sistema come
DayOfWeek

una volta ho un caso in cui il metodo con questo attributo è stato inserito nell'assembly generato (DateTime.AddYears, .Net 4.5)
gdbdable,

Risposte:


139

Non è documentato, ma sembra una delle ottimizzazioni in .NET 4.5. Sembra essere utilizzato per adescare la cache di informazioni sul tipo di riflessione, rendendo più veloce il successivo codice di riflessione su tipi di framework comuni. C'è un commento al riguardo nell'origine di riferimento per System.Reflection.Assembly.cs, proprietà 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;
 }

Senza ulteriori suggerimenti sul significato di "API benedetta". Sebbene sia chiaro dal contesto che questo funzionerà solo sui tipi nel framework stesso. Dovrebbe esserci un codice aggiuntivo da qualche parte che controlla l'attributo applicato a tipi e metodi. Non ho idea di dove si trovi, ma dato che dovrebbe avere una visione di tutti i tipi .NET per avere una possibilità di memorizzazione nella cache, posso solo pensare a Ngen.exe.


7
Sembra che il valore memorizzato venga utilizzato per verificare se l'API è disponibile su WP8.
usr

1
+1 Vedi il mio commento sulla Q del PO - un caso in cui il CLR sembra fare dei trucchi basati su questo è nella gestione di 'lievi' mosse di metodi (ad esempio giù di un livello su una nuova classe di base) sotto l'unificazione
Ruben Bartelink

2
Questo è il trucco [TypeForwardTo], qualcosa di completamente diverso.
Hans Passant,

@HansPassant Interessante - sembra che potrei sbagliarmi, quindi ... non avevo pensato di esaminare l'assemblaggio / tipo originale. La linea di fondo è che su 4.5 la proprietà citata (non il tipo) si è spostata rispetto a dove era su 3.5 (tecnicamente, System.ServiceModel 3.0). Avevo supposto che mscorlibfosse in gioco l' unificazione alla guida, ma ho comunque un sacco di volte a tornare indietro sul mio problema specifico da fare comunque -
riferirò

1
@HansPassant Da ulteriori ricerche ... Non riesco a vedere nulla in merito all'inoltro dei tipi che fa cose diverse dai tipi di inoltro, quindi a questo punto comincio a differire da qualcosa di completamente diverso . Le forze al lavoro sono semplicemente che quando si fa riferimento a un assembly CLR2 System.ServiceModel v3, caricandolo in CLR4 si aggiorna automaticamente System.ServiceModel v4. La cosa divertente è che .NET 4.5 esegue un aggiornamento sul posto dei bit di System.ServiceModelrilascio in una nuova classe di base sottostante e sposta la proprietà di un livello inferiore .
Ruben Bartelink,

23

Ho scoperto che è utilizzato nella Runtime*Info.IsNonW8PFrameworkAPI()suite di metodi interni. Il fatto che questo attributo sia inserito in un membro rende IsNonW8PFrameworkAPI () restituito falseper esso e quindi rende il membro disponibile nelle applicazioni WinRT e chiude ilThe API '...' cannot be used on the current platform. eccezione.

Gli autori di profili dovrebbero posizionare questo attributo sui membri emessi dal loro profilo in assiemi di framework, se desiderano accedervi con WinRT.


1
Sì, il codice trovato da @Hans imposta i flag cercati RuntimeAssembly.InvocableAttributeCtorToken, che viene chiamato dai IsNonW8PFrameworkAPI()metodi che menzioni.
Mark Hurd,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.