字段上的 PInvoke 实现

PInvoke Implementation on Fields

所以今天我在浏览 ILSpy 以更好地了解 .NET 如何对外部方法执行 DllImports,当我遇到一些奇怪的事情时:

在搜索对 System.Reflection.MethodAttributes 枚举中定义的枚举值 PInvokeImpl 的引用时,我注意到 System.Reflection.FieldAttributes.

中的匹配定义

果然,这似乎不仅仅是在幕后重复使用枚举值:System.Reflection.FieldInfo 有一个公开定义的 属性 称为 IsPinvokeImpl ,它专门检查是否设置了此实现标志。

有趣的是,MethodInfo class 甚至没有这个 属性 - 它必须从 MethodImplementationFlags 属性 来确定。

问题:

一个字段是否真的可以被 PInvoke 实现,或者这只是 .NET 框架中的一个存根实现,用于平衡字段装饰和方法装饰?

如果可能的话,可以用 C# 完成吗,或者这是一个需要 C++/CLI 的功能?

来自FieldAttributes

PinvokeImpl Reserved for future use.

所以我会说:

问:PInvoke 是否真的可以实现一个字段

答:没有

当您查看 FieldAttributes 的 MSDN 描述时,您会看到它被记录为 "Reserved for future use"。那个未来还没有到来,所以它的意图还没有确定。

FieldAttributes 等类型不是任意的,它们遵循 CLI 规范。 Ecma-335 明确了 .NET 程序集中的元数据应该是什么样子以及应该如何解释它。这份文件确实揭示了一个有趣的怪癖。

第 II.16.1 章描述了字段属性,您将看到元数据标记与 FieldAttributes 枚举之间的紧密匹配。但是请注意,pinvokeimpl 在该章中 缺失

第II.23.1.5章给出了属性的具体取值,其PInvokeImpl值为0x2000。说明是 "Implementation is forwarded through PInvoke"。与 II.23.1.10 相比,描述了方法属性。它有许多与字段属性相同的值。

这看起来很像 copy/paste 错误 :)

深入挖掘 .NET Framework 源代码,CLR 和抖动只考虑方法上的 pinvokeimpl。然而,C# 编译器似乎基于 CLI 规范并实际设置了属性。出现在emit.cpp、RegMeta::_DefinePinvokeMap()函数中,如果这个函数是为字段而不是方法调用的,它会设置属性。这从未真正发生过。