在我another question中,我发表了一条评论,表明.NET框架的Array.Copy
方法使用了非托管代码。我用Reflector挖掘并发现Array.Copy
方法重载的签名之一定义如下:
[MethodImpl(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
internal static extern void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable);
看着这个之后,我有点困惑。我混淆的原因是extern
修饰符,意思是(MSDN链接):
extern修饰符用于声明 一种实现的方法 外部
但是,方法声明也使用MethodImplOptions.InternalCall
属性进行修饰,该属性表示(MSDN链接):
指定内部呼叫。一个 内部调用是对方法的调用 这是在共同体内实施的 语言运行时本身。
任何人都可以解释这个看似明显的矛盾吗?
答案 0 :(得分:49)
我会对leppie's帖子发表评论,但这有点长。
我目前正在进行实验性CLI实施。在许多情况下,如果不了解内部如何实施虚拟机,则无法实现公开的方法(或属性)。一个例子是OffsetToStringData,它需要知道内存管理器如何分配字符串。
对于这样的情况,如果没有表示方法的C#代码,您可以以特殊的方式将对方法的每次调用视为 internal 到JIT进程。作为示例,在将call
字节代码传递给本机代码生成器之前,将ldc.i4
字节代码替换为InternalCall
(加载常量整数)。 Math
标志表示“运行时本身以特殊方式处理此方法的主体。”可能有也可能没有实际的实现 - 在我的代码中的几种情况下,JIT将调用视为intrinsic。
在其他情况下,JIT可能有特殊信息,可以对方法进行大量优化。一个例子是InternalCall
方法,即使这些can be implemented in C#,指定abstract
以使其有效地具有内在函数也具有显着的性能优势。
在C#中,除非extern
或extern
,否则方法必须包含正文。 extern
表示一般“你可以用C#代码调用这个方法,但它的主体实际上是在其他地方定义的。”当JIT到达DllImport
方法的调用时,它会查找找到正文的位置,并根据结果以不同的方式运行。
InternalCall
属性指示JIT创建P / Invoke存根以调用本机代码实现。答案 1 :(得分:16)
InternalCall
表示框架提供。
extern
表示您没有提供代码。
extern
可用于两种一般情况,如上所述,或使用p / invoke。
使用p / invoke,您只需告诉方法获取实现的位置。