早期和晚期绑定


83

当C#中发生早期/晚期绑定时,我想尽我所能。

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

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

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

我朝着正确的方向前进吗?

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

Answers:


102

除非您通过Reflection接口,否则所有内容都在C#中早期绑定。

早期绑定只是意味着在编译时找到目标方法,并创建将调用此方法的代码。无论它是虚拟的(意味着在通话时还有一个额外的步骤来查找它都是无关紧要的)。如果该方法不存在,则编译器将无法编译代码。

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

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

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

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


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

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

因此,存在针对虚拟功能的查找。这是经过高度优化的,因此它将在运行时非常迅速地发生。

早起

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

后期绑定

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

我认为您是想说“ VB语言本身不会延迟绑定……”
Michael Meadows

实际上,我不使用VB ...所以对此我不太了解。我的意思是C#,但看起来我只是在重复自己。我可以想像,尽管您是正确的,所以我将其修复!
Scott Langham

21
动态键入与后期绑定不同。差异是细微的,但很重要。后期绑定仍然绑定到类型,它只是在运行时执行。动态类型不绑定;而是在运行时解析成员信息,而与类型无关。
Michael Meadows

1
对于具有任何虚拟方法的对象,编译器将生成一个v表。 ”。这有点不正确-“类”而不是“对象”。
turdus-merula 2014年

1
@IvanRuski我不这么认为。在编译时,委托接受的所有参数类型都是已知的。因此,在编译时(即“较早”)而不是运行时(即“较晚”),编译器可以保证该调用将起作用。
Scott Langham

18

C#3使用早期绑定。

C#4使用dynamic关键字添加了后期绑定。有关详细信息,请参见Chris Burrow的博客条目

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


1
我不同意这种解释。在C#中,将实例方法或字段标记为虚拟均值派生类型可以覆盖继承链中的基本类型实现。使用虚拟方法,CLR根据运行时对象实例知道在运行时调用哪种方法。我唯一同意的地方可能是多态性的实现。然后,您说这不是后期绑定会引起混乱。这是后期绑定,因为CLR仅在知道对象实例的运行时类型时才能调用正确的运行时类型实现。
Julius Depulla '16

6

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

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

另一方面,如果您在编译时不了解该类,则唯一的方法是使用反射进行后期绑定。我看过一段精彩的视频,解释了这些概念-这是链接


3

它是一个非常老的帖子,但想向它添加更多信息。如果您不想在编译时实例化对象,则使用后期绑定。在C#您用来Activator在运行时调用绑定对象。


3

早期绑定

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

常见示例:

ComboBox cboItems;

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

后期绑定

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

常见示例:

对象objItems;

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

早期绑定与后期绑定

现在进入图片……

由于此处没有装箱或拆箱,因此应用程序在早期绑定中的运行速度会更快。

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

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

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

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

性能将是早期绑定中的代码。两者都有优点和缺点,这是开发人员决定根据情况选择合适的绑定的决定。



1

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

http://www.codeproject.com/KB/cs/csapivb6callback2.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会为我们完成此操作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.