35

BCL 中的许多方法都标有该 [MethodImpl(MethodImplOptions.InternalCall)]属性。这表明“方法是在公共语言运行时本身内实现的”。

以这种方式设计框架而不是指定运行时将被迫实现的显式 CIL 指令有什么意义?最终,该属性为运行时创建了合同义务,但在我看来,这种方式令人困惑,而且不是立即显而易见的。

例如,Math.Pow可以这样写(如果我的 C# + IL 和 IL 本身不好,请原谅我的非正式混合;这只是解释我的观点的一个示例):

public static double Pow(double x, double y)
{
    ldarg.0
    ldarg.1
    pow // Dedicated CIL instruction
    ret
}

而不是当前的方式:

[MethodImpl(MethodImplOptions.InternalCall)]
public static double Pow(double x, double y);

为什么MethodImplOptions.InternalCall存在?

4

4 回答 4

9

我认为一个重要的原因是创建一个新的 IL 指令非常困难,它可能会影响很多工具,包括外部工具(ILGenerator、ilasm、ildasm、PEVerify、Reflector、PostSharp,...)。

但是创建一个新InternalCall方法?这几乎就像在 C# 中编写方法一样简单(我假设,我没有查看 Rotor 来验证)并且它不会影响任何东西。

这不仅仅是关于创建它,我认为这同样适用于维护。

于 2012-06-22T18:02:14.120 回答
4

我认为这是为了不要使 CLR 过于复杂。当我第一次研究 CIL 时,我不禁注意到与汇编语言的相似之处,不仅如此,有限的指令集,几乎就像它们让它直接在处理器上运行一样。

从理论上讲,当 CLR JIT 的示例代码Pow包含在您的帖子中时,它必须向自己发布指令的本机代码pow,因为没有本机指令(或者是它?还没有与新的 x86 保持同步)几年前的指令集)。从性能和实现的角度来看,只为pow代码调用 mscorlib 比直接“粘贴”它要容易得多。

或者,他们可以有一个用于常见 mscorlib 函数的查找表,这些函数是InternalCall并且用对函数本身的调用来替换指令。但话又说回来,并非所有事情InternalCall都那么容易。

我认为这是为了方便而进行的权衡;对于 CLR 的可维护性,更统一的 CLI 标准并允许调用者具有一定的灵活性。

底线是我还没有开发 CLR,这只是我的想法。我想我会做的事情。

于 2012-06-22T17:34:43.837 回答
4

该方法是原生的,真正原生的,在运行时本身中实现。不要忘记,CLR 毕竟来自 C++。就像在编译器中一样。在现实世界中,CIL 并未真正执行。它是 JIT-ted,然后像编译器一样由 C/C++ 运行时执行。Math.Pow 很可能是 [推测] 对本机 C/C++ math.h pow 方法的调用,它是实现定义的——现在 NET 和 Mono 实现了 CLR。

在 UnityEngine.dll 中,[MethodImpl(MethodImplOptions.InternalCall)]用于大多数本机外部方法。然而,这些 C++ 方法直接使用 Mono C++ CLR 库,并且它们可以以比 P/INVOKE 本身更亲密的方式与 C# 协作。而且性能更高(PInvoke 隐藏了一些实现细节,使一些难以理解)

但是,唯一的使用方法[MethodImpl(MethodImplOptions.InternalCall)]是如果您是 CLR 运行时本身 - 但是我只测试过这样的 Mono。我不知道是否有可能更改 Microsoft 的 CLR 实现,但使用 Mono,您可以随意滥用此功能。

另外,不要把这个弄乱[MethodImpl(MethodImplOptions.Unmanaged)]- 这是完全不同的故事。

有关内部调用和 Mono 工作原理的更多信息,请访问:http ://www.mono-project.com/docs/advanced/embedding/

免责声明:我与 Unity 或 Mono 无关!

于 2017-01-27T22:35:01.413 回答
1

它是一个 C# 属性(向单声道运行时表明它是对本机方法的调用),它调用与嵌入单声道运行时的可执行文件链接的 .dll 中的本机 C/C++ 代码(并且 .dll 导出函数)或在嵌入运行时的可执行文件中(在 DLLImport 中使用“__Internal”)。

它是单声道调用本机代码的两种方法之一,一种是 P/invoke(使用 DLLImport),另一种是内部调用(MethodImplOptions.InternalCall)。P/invoke 提供编组,而内部调用仅编组 blittable 类型。见这里:https ://www.mono-project.com/docs/advanced/embedding/ 。

When the virtual machine detects a C# attribute that indicates an internal call, it will look up the name of the function in its database of pairings (built by the c++ code e.g. mono_add_internal_call ("MonoEmbed::gimme", (const void *)gimme)) and it will then call the address of the function. For Dllimport, it will make API calls LoadLibrary and GetProcAddress for the .dll or if __Internal, passes hModule = GetModuleHandle(NULL) for its own module to GetProcAddress, which will be your C++ executable it you embedded mono (linked libmono statically), and if you linked it dynamically then your have to specify the .exe name I'd imagine.

于 2020-03-28T22:34:26.030 回答