早期和晚期绑定

79 c# binding late-binding

当我在C#中发生早期/晚期绑定时,我正试图解决这个问题.

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

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

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

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

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

Sco*_*ham 98

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

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

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

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

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

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


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

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

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

早期约束

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

后期绑定

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

  • 动态类型与后期绑定不同.差异很微妙,但很重要.后期绑定仍然绑定到一个类型,它只是在运行时执行.动态类型不绑定; 相反,它在运行时解析成员信息,而不管类型如何. (21认同)

Joe*_*son 17

C#3使用早期绑定.

C#4使用dynamic关键字添加后期绑定.有关详细信息,请参阅Chris Burrow关于此主题的博客文章.

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


小智 5

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

//Early Binding
**Employee** employeeObject = new **Employee**();
employeeObject.CalculateSalary();
Run Code Online (Sandbox Code Playgroud)

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