“__DynamicallyInvokable”属性的作用是什么?

199

在DotPeek中查看System.Linq.Enumerable时,我注意到一些方法带有[__DynamicallyInvokable]属性。

这个属性有什么作用?它是DotPeek添加的吗?还是它发挥了另一种作用,比如通知编译器如何最好地优化这些方法?


2
String.Empty 也有这个,顺便说一下。 - Marc Gravell
1
IReadOnlyCollection<T> 也是如此。 - Drew Noakes
1
System.ServiceModel v3中的BasicHttpBinding.TextEncoding(在V4中已经移动到一个新的基类,并成为HttpBindingBase.TextEncoding)。 - Ruben Bartelink
它还用于System枚举中的整数值,例如DayOfWeek。 - beauXjames
我曾经遇到过这样的情况,当时带有此属性的方法在生成的汇编代码中被内联了(DateTime.AddYears,.Net 4.5)。 - gdbdable
3个回答

152

这是一个未记录的优化功能,但看起来它是.NET 4.5中的其中一个优化。它似乎用于预加载反射类型信息缓存,使得在常见框架类型上运行后续反射代码更快。在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;
 }

没有进一步的提示,"blessed API"可能意味着什么。尽管从上下文中清楚地表明这只适用于框架本身中的类型。应该有其他代码检查应用于类型和方法的属性。我不知道它位于何处,但考虑到它必须需要查看所有.NET类型才能尝试缓存,我只能想到Ngen.exe。


9
似乎正在使用存储的值来检查 API 是否在 WP8 上可用。 - usr
1
+1 看到我在 OP 的问题上的评论了吗?CLR 在处理方法的“轻微”移动(例如向下移动一个级别到新的基类)时似乎会进行一些诡计。 - Ruben Bartelink
4
那就是 [TypeForwardTo] 技巧,完全不同的东西。 - Hans Passant
1
从进一步的研究中发现...看不到任何关于类型转发做其他事情的东西,除了转发类型,所以在这一点上我不同意完全不同的东西。工作中的力量很简单,当您有一个CLR2程序集引用System.ServiceModel v3时,在CLR4下加载它会自动升级为System.ServiceModel v4。有趣的是,.NET 4.5对System.ServiceModel的位进行了就地更新,并在下面移动了一个新的基类和属性 - Ruben Bartelink
我认为属性的存在在使这种重定向相对透明地被管理方面发挥了作用。请注意,该属性出现在属性本身以及单个属性getter和属性setter上。请注意,GetMethods()仅在新位置显示它,而不考虑exe或dll的supportedRuntime以及我的逻辑所在的位置。 - Ruben Bartelink
显示剩余3条评论

27

我发现它被用于Runtime*Info.IsNonW8PFrameworkAPI()一套内部方法中。 将此属性放置在成员上使得IsNonW8PFrameworkAPI()返回false,使该成员在WinRT应用程序中可用,并消除了The API '...' cannot be used on the current platform.异常。

如果Profiler编写者想要在WinRT下访问由其分析器发出到框架程序集中的成员,则应将此属性放置在这些成员上。


2
是的,@Hans 找到的代码设置了 RuntimeAssembly.InvocableAttributeCtorToken 所寻找的标志,后者由你提到的 IsNonW8PFrameworkAPI() 方法调用。 - Mark Hurd
经运行时团队确认,此问题与Windows Store有关:https://github.com/dotnet/runtime/issues/30809 - undefined

-1

抱歉回复晚了。此属性用于从托管代码调用非托管代码函数。它用于将托管语言与非托管语言分离,形成两个世界的屏障。它还用于安全原因,以使非托管代码反射不可访问。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接