¿Para qué sirve el atributo __DynamicallyInvokable?

181

Mirando a través System.Linq.Enumerablede DotPeek, noto que algunos métodos tienen sabor a [__DynamicallyInvokable]atributo.

¿Qué papel juega este atributo? ¿Es algo agregado por DotPeek o desempeña otro papel, quizás informando al compilador sobre la mejor manera de optimizar los métodos?

Jamie Dixon
fuente
2
String.Empty también tiene esto, por cierto.
Marc Gravell
1
También lo hace IReadOnlyCollection<T>.
Drew Noakes
1
Y System.ServiceModel v3's BasicHttpBinding.TextEncoding(que en V4 se ha movido a una nueva clase base y se convierte en HttpBindingBase.TextEncoding)
Ruben Bartelink
también se usa para los valores enteros en enumeraciones del sistema como DayOfWeek
beauXjames
una vez que tengo un caso cuando el método con este atributo se insertó en el ensamblado generado (DateTime.AddYears, .Net 4.5)
gdbdable

Respuestas:

139

No está documentado, pero parece una de las optimizaciones en .NET 4.5. Parece que se usa para cebar la caché de información de tipo de reflexión, lo que hace que el código de reflexión posterior en los tipos de marco comunes se ejecute más rápido. Hay un comentario al respecto en la Fuente de referencia para la propiedad 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;
 }

Sin más pistas sobre lo que podría significar una "API bendecida". Aunque está claro por el contexto que esto solo funcionará en tipos en el marco mismo. Debería haber un código adicional en alguna parte que verifique el atributo aplicado a los tipos y métodos. No tengo idea de dónde se encuentra, pero dado que tendría que tener una vista de todos los tipos .NET para tener una oportunidad de almacenamiento en caché, solo puedo pensar en Ngen.exe.

Hans Passant
fuente
77
Parece que el valor almacenado se está utilizando para verificar si la API está disponible en WP8.
usr
1
+1 Vea mi comentario sobre la Q del OP: un caso en el que el CLR parece estar haciendo trucos en base a esto es en el manejo de movimientos 'leves' de métodos (por ejemplo, un nivel inferior a una nueva clase base) bajo unificación
Ruben Bartelink
2
Ese es el truco [TypeForwardTo], algo completamente diferente.
Hans Passant
@HansPassant Interesante: parece que podría estar equivocado, así que ... no había pensado en examinar el ensamblaje / tipo original. La conclusión es que en 4.5 la propiedad citada (no el tipo) se ha movido en relación a donde estaba en 3.5 (técnicamente System.ServiceModel 3.0). Supuse que la unificación a la mscorlibreferencia estaba en juego, pero tengo que dar muchas vueltas sobre mi problema específico para hacer de todos modos: informaré y / o eliminaré cualquier tono engañoso de mis comentarios a su debido tiempo ...
Ruben Bartelink
1
@HansPassant De una investigación más profunda ... No puedo ver nada relacionado con el reenvío de tipos haciendo cosas que no sean tipos de reenvío, así que en este punto ruego diferir con algo completamente diferente . Las fuerzas en el trabajo son simplemente que cuando tiene una referencia de ensamblaje CLR2 System.ServiceModel v3, lo carga bajo actualizaciones automáticas CLR4 a System.ServiceModel v4. Lo divertido es que .NET 4.5 realiza una actualización in situ de los bits de System.ServiceModelcaída en una nueva clase base debajo y mueve la propiedad a un nivel inferior .
Ruben Bartelink
23

Descubrí que se usa en el Runtime*Info.IsNonW8PFrameworkAPI()conjunto de métodos internos. Tener este atributo colocado en un miembro hace que IsNonW8PFrameworkAPI () regrese falsepor él y, por lo tanto, hace que el miembro esté disponible en las aplicaciones WinRT y cierra la The API '...' cannot be used on the current platform.excepción.

Los escritores de perfiles deben colocar este atributo en los miembros emitidos por su generador de perfiles en ensamblajes de marco, si desean acceder a ellos en WinRT.

Stefan Dragnev
fuente
1
Sí, el código encontrado por @Hans configura las banderas buscadas RuntimeAssembly.InvocableAttributeCtorToken, que se llama por los IsNonW8PFrameworkAPI()métodos que mencionas.
Mark Hurd el