88

当 C# 中发生早期/晚期绑定时,我正试图弄清楚。

非虚拟方法总是早期绑定的。虚拟方法总是后期绑定的:编译器插入额外的代码来解析在执行时绑定到的实际方法并检查类型安全。所以亚型多态性使用后期绑定。

使用反射调用方法是后期绑定的一个例子。我们编写代码来实现这一点,而不是编译器。(例如调用 COM 组件。)

当 Option Strict 关闭时,VB.NET 支持隐式后期绑定。当一个对象被分配给一个声明为 Object 类型的变量时,它是后期绑定的。VB 编译器插入代码以在执行时绑定到正确的方法并捕获无效调用。C# 不支持此功能。

我是否朝着正确的方向前进?

调用委托和通过接口引用调用方法呢?是早绑定还是晚绑定?

4

7 回答 7

106

除非您通过反射接口,否则一切都在 C# 中早期绑定。

早期绑定只是意味着在编译时找到目标方法,并创建将调用它的代码。它是否是虚拟的(这意味着在调用时需要额外的步骤来找到它是无关紧要的)。如果该方法不存在,编译器将无法编译代码。

后期绑定意味着在运行时查找目标方法。通常使用方法的文本名称来查找它。如果方法不存在,砰。程序将在运行时崩溃或进入一些异常处理方案。

大多数脚本语言使用后期绑定,而编译语言使用早期绑定。

C#(版本 4 之前)没有后期绑定;然而,他们可以使用反射 API 来做到这一点。该 API 编译为通过在运行时挖掘程序集来查找函数名称的代码。如果 Option Strict 关闭,VB 可以后期绑定。

绑定通常会影响性能。因为后期绑定需要在运行时查找,这通常意味着方法调用比早期绑定方法调用慢。


对于普通函数,编译器可以计算出它在内存中的数字位置。然后它在调用函数时可以生成一条指令来调用该地址处的函数。

对于具有任何虚方法的对象,编译器将生成一个 v-table。这本质上是一个包含虚拟方法地址的数组。每个具有虚方法的对象都将包含一个由编译器生成的隐藏成员,它是 v-table 的地址。当调用虚函数时,编译器会计算出相应方法在 v-table 中的位置。然后它将生成代码以查看对象 v-table 并在此位置调​​用虚拟方法。

因此,对虚拟功能进行了查找。这是经过高度优化的,因此它会在运行时很快发生。

早绑定

  • 编译器可以在编译时计算出被调用函数的位置。
  • 编译器可以尽早(在任何程序代码运行之前)保证该函数将存在并在运行时可调用。
  • 编译器保证函数采用正确数量的参数并且它们是正确的类型。它还检查返回值的类型是否正确。

后期装订

  • 查找将花费更长的时间,因为它不是简单的偏移计算,通常需要进行文本比较。
  • 目标函数可能不存在。
  • 目标函数可能不接受传递给它的参数,并且可能具有错误类型的返回值。
  • 对于某些实现,目标方法实际上可以在运行时更改。因此,查找可能会执行不同的功能。我认为这发生在 Ruby 语言中,您可以在程序运行时在对象上定义一个新方法。后期绑定允许函数调用开始为方法调用新的覆盖,而不是调用现有的基方法。
于 2009-01-27T17:03:31.600 回答
18

C# 3 使用早期绑定。

dynamicC# 4 使用关键字添加后期绑定。有关详细信息,请参阅Chris Burrow关于该主题的博客条目。

至于虚拟与非虚拟方法,这是一个不同的问题。如果我调用string.ToString(),则 C# 代码将绑定到虚拟object.ToString()方法。调用者的代码不会根据对象的类型而改变。相反,虚拟方法是通过函数指针表调用的。对象的实例是指指向其ToString()方法的对象表。一个字符串的实例有它的虚拟方法表指向它的ToString()方法。是的,这就是多态性。但它不是后期绑定。

于 2009-01-27T17:53:04.277 回答
6

在大多数情况下,早期绑定是我们每天都在做的事情。例如,如果我们有一个Employee在编译时可用的类,我们只需创建该类的实例并调用任何实例成员。这是早期绑定。

//Early Binding
**Employee** employeeObject = new **Employee**();
employeeObject.CalculateSalary();

另一方面,如果您在编译时不了解类,那么唯一的方法是使用反射进行后期绑定。我遇到了一个解释这些概念的精彩视频——这里是链接

于 2012-07-09T14:57:44.610 回答
3

Its a very old post but wanted to add more info to it. Late binding is used when you dont want to instantiate object at compile time. In C# you use Activator to call bind object at runtime.

于 2012-09-27T18:45:11.963 回答
3

早期绑定

这个名字本身描述了编译器知道它是什么类型的对象,它包含的所有方法和属性是什么。声明对象后,.NET Intellisense 将在单击点按钮时填充其方法和属性。

常见示例:

组合框 cboItems;

列表框 lstItems; 在上面的示例中,如果我们键入 cboItem 并在后面放置一个点,它将自动填充组合框的所有方法、事件和属性,因为编译器已经知道它是一个组合框。

后期装订

这个名字本身描述了编译器不知道它是什么类型的对象,它包含的所有方法和属性是什么。您必须将其声明为对象,稍后您需要获取对象的类型,存储在其中的方法。一切都将在运行时知道。

常见示例:

对象对象;

objItems = CreateObject("DLL 或程序集名称"); 在编译期间,objItems 的类型是不确定的。我们正在创建一个 dll 的对象并将其分配给 objItems,因此一切都在运行时确定。

早期绑定与后期绑定

现在进入画面......</p>

应用程序将在早期绑定中运行得更快,因为这里没有装箱或拆箱。

更容易在早期绑定中编写代码,因为智能感知将自动填充

早期绑定中的最小错误,因为在编译时检查语法。

后期绑定将支持所有类型的版本,因为一切都是在运行时决定的。

如果使用后期绑定,则代码对未来增强的影响最小。

性能将是早期绑定中的代码。两者各有优缺点,根据场景选择合适的绑定是开发者的决定。

于 2017-05-25T07:25:44.570 回答
2

简单来说,早期绑定发生在编译时,编译器知道类型及其所有成员,后期绑定发生在运行时,编译器对类型及其成员一无所知。我在 youtube 上看到了一个很好的视频,它解释了这些概念。

http://www.youtube.com/watch?v=s0eIgl5iqqQ&list=PLAC325451207E3105&index=55&feature=plpp_video

http://www.youtube.com/playlist?list=PLAC325451207E3105

于 2012-09-04T12:14:42.553 回答
1

本文是构建 .net 组件的指南,在运行时使用后期绑定在 Vb6 项目中使用它,附加它的事件并获取回调。

http://www.codeproject.com/KB/cs/caspivb6callback2.aspx

本文是构建 .NET 组件并在 VB6 项目中使用它的指南。关于这个问题的示例有很多,那我为什么要写一个新的呢?在我看来,在其他文章中,缺少的部分是在运行时附加其事件。所以在本文中,我们将构建一个 .NET 组件,将其标记为 COM 可见组件,在 VB6 运行时使用它并附加到它的事件。

https://www.codeproject.com/Articles/37127/Internet-Explorer-Late-Binding-Automation

大多数开发人员经常需要 Internet Explorer 自动化,这基本上意味着以编程方式打开浏览器、填写一些表单和发布数据。

最常见的方法是使用 shdocvw.dll(Microsoft Web 浏览器控件)和 Mshtml.dll(HTML 解析和呈现组件),或 Microsoft.Mshtml.dll,它实际上是 Mshtml.dll 的 .NET 包装器。您可以在此处获取有关 Internet Explorer - 关于浏览器的更多信息。

如果您选择上述方法和 DLL,让我们看看您可能需要处理的一些问题:

您必须分发这些 DLL,因为您的项目将依赖于这些 DLL,如果您无法正确部署它们,这将是一个严重的问题。只需对 shdocvw 和 mshtml.dll 分发问题进行一些谷歌搜索,您就会明白我在说什么。您必须部署一个 8 MB Microsoft.mshtml.dll,因为此 DLL 不是 .NET 框架的一部分。在这种情况下,我们需要做的是使用后期绑定技术。为上述 DLL 编写我们自己的包装器。当然,我们会这样做,因为它比使用这些 DLL 更有用。例如,我们不需要检查文档下载操作是否完成,因为 IEHelper 会为我们完成这项工作。

于 2010-04-26T08:48:26.410 回答