什么时候使用 extern 关键字而不使用 [DllImport] 属性是合适的?

When is it appropriate to use the extern keyword without using the [DllImport] attribute?

我今天重新阅读一些 .Net 文档时注意到 extern keywords documentation 声明的第一部分:

The extern modifier is used to declare a method that is implemented externally. A common use of the extern modifier is with the DllImport attribute when you are using Interop services to call into unmanaged code.

引起我注意的是文档指出 extern 的 "a common use" 是与 DllImport 属性一起使用的。这意味着还有其他不需要 DllImport 的用例。我不必将许多外部非托管库集成到我的应用程序中,但在所有情况下,链接方法都是使用 DllImport 定义的。

我已经通过 Google 和 MSDN 搜索了多个查询,但找不到在不将方法定义为外部方法导入的情况下何时使用 extern 关键字的案例或解释来自非托管 dll。

如何以及何时使用 extern 关键字而不在方法定义中定义 [DllImport(...)] 属性?

请注意,这并不特定于在定义别名时使用 extern。这是关键字的不同用法,这种情况在 MSDN C# 语言参考中是 outlined in a different article

如果我是一名 Microsoft 开发人员,我会使用它来实现对 CLR 本身中定义的方法的调用。就像 GC._WaitForFullGCApproach:

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern int _WaitForFullGCApproach(int millisecondsTimeout);

注:无DllImport。当然这有点作弊——这仍然是对非托管方法的调用,只是没有显式引用 DLL。但是,凡人无法调用此类代码,因为它仅在 mscorlib 程序集中有效。

InternalCall 的另一个应用是为 COM 生成的互操作类型:

namespace Microsoft.Office.Interop.Excel {
    [DefaultMember("_Default")]
    [ClassInterface(0)]
    [ComSourceInterfaces("Microsoft.Office.Interop.Excel.AppEvents[=11=]")]
    [Guid("00024500-0000-0000-C000-000000000046")]
    [TypeLibType(2)]
    [ComImport]
    public class ApplicationClass {
        // ...
        [DispId(302)]
        [MethodImpl(MethodImplOptions.InternalCall)]
        public virtual extern void Quit();
        // ...
    }
}

这些属性允许运行时将方法调用解析为对 COM 接口的调用。显然,InternalCall mscorlib 之外有效。您通常不会自己用 C# 编写此类代码;它是在您添加 COM 类型库作为引用时按需生成的。

C# 语言规范比 MSDN 更详细:

The extern modifier is typically used in conjunction with a DllImport attribute (§17.5.1), allowing external methods to be implemented by DLLs (Dynamic Link Libraries). The execution environment may support other mechanisms whereby implementations of external methods can be provided.

从实现的角度来看,标记一个方法extern只是将方法的RVA(相对虚拟地址)设置为0,标记为没有实现。像 DllImport(和 MethodImpl)这样的属性对于向运行时描述如何定位方法的实际实现是必要的。这在 ECMA-335 的第 I.9.4 节中有描述,"Method implementation metadata"(DllImportInternalCall 似乎是目前唯一可用的方法)。

C# 编译器将允许您将方法标记为 extern 使用任何属性来指示实现所在的位置,但任何具有此类方法的类型将在运行时产生 TypeLoadException