早期和晚期绑定

时间:2009-01-27 16:56:35

标签: c# binding late-binding

我正试图在C#中发生早期/晚期绑定时解决问题。

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

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

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

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

如何通过接口引用调用委托并调用方法?是早期还是晚期绑定?

7 个答案:

答案 0 :(得分:95)

除非你通过Reflection接口,否则一切都在C#中提前绑定。

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

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

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

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

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


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

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

因此,虚拟函数会发生查找。这是经过大量优化的,因此在运行时会很快发生。

早期绑定

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

后期绑定

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

答案 1 :(得分:16)

C#3使用早期绑定。

C#4使用dynamic关键字添加后期绑定。有关详细信息,请参阅主题Chris Burrow's blog entry

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

答案 2 :(得分:5)

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

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

另一方面,如果您在编译时没有获得该类的知识,那么唯一的方法是使用反射进行后期绑定。我遇到了一个很好的视频来解释这些概念 - here's the link

答案 3 :(得分:3)

这是一个非常古老的帖子,但想要添加更多信息。当你不想在编译时实例化对象时,使用后期绑定。在C#中,您使用Activator在运行时调用绑定对象。

答案 4 :(得分:3)

早期绑定

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

常见例子:

ComboBox cboItems;

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

晚期绑定

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

常见例子:

对象objItems;

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

早期结合与晚期结合

现在进入图片

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

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

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

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

如果使用Late Binding,则代码在未来增强中的影响最小。

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

答案 5 :(得分:2)

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

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

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

答案 6 :(得分: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将为我们执行此操作。