【问题标题】:How does extern work in C#?extern 在 C# 中是如何工作的?
【发布时间】:2011-07-03 21:36:48
【问题描述】:

每当我深入研究反射器时,我都会遇到没有来源的extern 方法。我在http://msdn.microsoft.com/en-us/library/e59b22c5(v=vs.80).aspx 阅读了msdn 文档。我从那篇文章中得到的是必须注入带有extern 修饰符的方法。我将其解释为它的工作原理类似于抽象工厂模式。我还注意到我从未见过非静态外部方法。静态声明是必需的吗(我可以看到这有什么意义)?我仍然在这里猜测,我不确定它实际上是如何工作的。在我看来,编译器必须识别某些减轻处理的属性,但除了我从 MSDN 示例中遇到的 MethodImplAttributeDllImportAttribute 之外,我不知道这些属性是什么。有人如何利用extern 属性?它说,在许多情况下,这可以提高性能。另外,我将如何查看extern 方法的来源,例如Object.InternalGetEquals()

【问题讨论】:

  • MemberwiseClone() .Net 中 System.Object 类的方法是 extern 但不是 static
  • 当外部方法包含DllImport 属性时,方法声明还必须包含静态修饰符。

标签: c# .net performance extern modifier


【解决方案1】:

考虑阅读 C# 规范的第 10.6.7 节,它回答了您的许多问题。为了您的方便,我在这里复制了一部分:


当方法声明包含 extern 修饰符,据说那个方法 成为一种外部方法。外部的 方法在外部实现, 通常使用的语言不是 C#。因为外部方法 声明没有提供实际的 实现,方法体 外部方法简单地由一个 分号。外部方法可能不 是通用的。外部修饰符是 通常与一个结合使用 DllImport 属性, 允许外部方法 由 DLL 实现(动态链接 图书馆)。执行环境 可能支持其他机制,从而 外部方法的实现 可以提供。当外部 方法包括一个 DllImport 属性, 方法声明也必须 包括一个静态修饰符。


有人如何利用 extern 属性?

  • 用您选择的非托管语言编写代码。
  • 将其编译为 DLL,导出代码的入口点。
  • 创建一个互操作库,将该方法定义为给定 DLL 中的外部方法。
  • 从 C# 调用它。
  • 利润!

我将如何着手研究像 Object.InternalGetEquals() 这样的外部方法的来源?

转到https://github.com/dotnet/coreclr/tree/master/src/vm

【讨论】:

  • SSLI20 源代码对于像我这样的凡人来说仍然是内部方法的相当准确的副本。无论如何,足以回答一堆 CLR 问题。查看 clr/src/vm/ecall.cpp 以了解从框架名称到 C++ 函数的映射。下载地址:microsoft.com/downloads/en/…
  • 很好的答案,你让我笑了
  • @Hans:请注意,在查看 SSCLI 之后,您不能为 Mono 项目做出贡献。
  • 嗯,你最好也远离微软员工的回答。
  • @Eric,为什么外部方法可能不是通用的?我注意到 C# 编译器可以很好地编译通用外部方法。我正在研究一个在类中插入方法的post-compiler。所以我在考虑使用extern 作为后编译器插入代码的占位符。我不认为extern 被设计为[DllImport] 一起使用,是吗?
【解决方案2】:

我们在方法声明中使用“extern”修饰符。它用于表示该方法是在外部实现的。 “extern”修饰符的一个常见用途是与 DllImport 属性一起使用。使用此属性管理非 C# 函数调用。 如果您使用的是 extern 修饰符,那么您必须包含以下命名空间:

using System.Runtime.InteropServices;

语法类似于:

[DllImport("User32.dll")] public static extern int MessageBox(int h, string m, string c, int type);

【讨论】:

  • 丹·阿布拉莫夫有什么不同的说法吗?
【解决方案3】:

[DllImport] 属性标记extern 的方法通常是对C 库的调用。此功能对于调用 WinAPI 或遗留代码很有用。

这是来自 MSDN 的示例:

using System;
using System.Runtime.InteropServices;
class MainClass 
{
   [DllImport("User32.dll")]
   public static extern int MessageBox(int h, string m, string c, int type);

   static int Main() 
   {
      string myString; 
      Console.Write("Enter your message: ");
      myString = Console.ReadLine();
      return MessageBox(0, myString, "My Message Box", 0);
   }
}

它调用在 Windows user32.dll 库中定义的 MessageBox。运行时在这里为您完成所有繁重的工作,尽管有时您需要手动管理内存。如果你得到错误的签名,你的程序可能会在调用中失败,你可能会引入泄漏或者方法可能会返回完全不同的东西,所以要小心!我发现 pinvoke.net 是一个很好的工具来纠正不同 API 的签名。

.NET Framework 中一些没有[DllImport] 属性但用[MethodImpl (MethodImplOptions.InternalCall)] 属性修饰的extern 方法通常是在CLR 本身中实现的,也是用C 编写的。有些这样的方法无法在 C# 中实现,因为它们自己管理运行时,而有些则在 C 中实现,因为它们的性能很关键,而且 C 更快。

这是 MSDN says 关于他们的:

指定内部调用。内部调用是对在公共语言运行时本身内实现的方法的调用。

至于查看实际的实现代码,我怀疑你能否从 Microsoft 获得它,但有 some cool alternative implementations of CLR around 所以一定要查看它们。

【讨论】:

  • 有一点要记住,在使用 extern 和 DLLImport 时,您经常会发现对“不安全”的引用。请记住,这允许您使用诸如实际指针之类的东西,并且在编写自己的外部方法时,您必须非常小心,以免引起任何内存泄漏。
【解决方案4】:

extern 带有平台调用 (pinvoke) 以促进托管程序集调用非托管代码。 extern 关键字通知编译器它需要生成正确的代码以允许正确的数据封送处理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-29
    • 2019-12-22
    • 1970-01-01
    • 2023-03-24
    • 2013-12-29
    • 1970-01-01
    • 2014-03-20
    • 1970-01-01
    相关资源
    最近更新 更多