Para que serve o atributo __DynamicallyInvokable?

181

Examinando o System.Linq.EnumerableDotPeek, percebo que alguns métodos têm um [__DynamicallyInvokable]atributo.

Que papel esse atributo desempenha? É algo adicionado pelo DotPeek ou desempenha outro papel, talvez informando o compilador sobre a melhor forma de otimizar os métodos?

Jamie Dixon
fonte
2
String.Empty também tem isso, btw.
Marc Gravell
1
O mesmo acontece IReadOnlyCollection<T>.
Drew Noakes
1
E System.ServiceModel v3's BasicHttpBinding.TextEncoding(que na V4 mudou para uma nova classe base e se torna HttpBindingBase.TextEncoding)
Ruben Bartelink 13/02/13
ele também é usado para os valores inteiros em enums sistema como DayOfWeek
beauXjames
uma vez que tem um caso quando método com este atributo foi embutido em gerados montagem (DateTime.AddYears, Net 4.5)
gdbdable

Respostas:

139

Não está documentado, mas parece uma das otimizações do .NET 4.5. Parece ser usado para preparar o cache de informações do tipo de reflexão, tornando o código de reflexão subsequente em tipos de estrutura comuns mais rápido. Há um comentário sobre isso na propriedade Reference Source for 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;
 }

Sem mais dicas, o que uma "API abençoada" pode significar. Embora seja claro, a partir do contexto, isso funcionará apenas em tipos na própria estrutura. Deveria haver um código adicional em algum lugar que verifique o atributo aplicado aos tipos e métodos. Não faço ideia de onde ele está localizado, mas, como seria necessário ter uma visualização de todos os tipos de .NET para ter uma chance de armazenar em cache, só consigo pensar em Ngen.exe.

Hans Passant
fonte
7
Parece que o valor armazenado está sendo usado para verificar se a API está disponível no WP8.
usr
1
+1 Veja meu comentário sobre o Q do OP - um caso em que o CLR parece estar fazendo truques com base nisso está no manuseio de movimentos 'leves' de métodos (por exemplo, descendo um nível para uma nova classe base) sob unificação
Ruben Bartelink
2
Esse é o truque [TypeForwardTo], algo completamente diferente.
Hans Passant
@HansPassant Interesting - parece que eu posso estar errado, então ... não tinha pensado em examinar a montagem / tipo original. A linha inferior é que em 4.5 a propriedade citada (não o tipo) se moveu em relação a onde estava em 3.5 (tecnicamente System.ServiceModel 3.0). Eu tinha assumido que a unificação à la mscorlibreferencia estava em jogo, mas ainda tenho muito o que fazer em meu problema específico - relatará e / ou removerá qualquer tom enganoso dos meus comentários no devido tempo ...
Ruben Bartelink
1
@HansPassant De pesquisas adicionais ... Não consigo ver nada sobre o Type Forwarding fazendo outras coisas que não o Forwarding Types, então neste momento eu imploro para diferir com o algo completamente diferente . As forças no trabalho são simplesmente que, quando você tem uma referência de montagem do CLR2 System.ServiceModel v3, carrega-a nas atualizações automáticas do CLR4 para System.ServiceModel v4. A parte divertida é que o .NET 4.5 faz uma atualização no local para os bits de System.ServiceModelsoltar em uma nova classe base abaixo e move a propriedade para um nível abaixo .
Ruben Bartelink
23

Descobri que é usado no Runtime*Info.IsNonW8PFrameworkAPI()conjunto de métodos internos. A colocação desse atributo em um membro faz com que IsNonW8PFrameworkAPI () retorne falsepara ele e, assim, torna o membro disponível nos aplicativos WinRT e fecha a The API '...' cannot be used on the current platform.exceção.

Os gravadores de criadores de perfil devem colocar esse atributo nos membros emitidos pelo criador de perfil nos assemblies de estrutura, se quiserem acessá-los no WinRT.

Stefan Dragnev
fonte
1
Sim, o código encontrado pelo @Hans configura os sinalizadores procurados RuntimeAssembly.InvocableAttributeCtorToken, chamados pelos IsNonW8PFrameworkAPI()métodos mencionados.
Mark Hurd