我不断听到有关早期和晚期绑定的信息,但我不了解它们是什么。我发现以下我不理解的解释:
早期绑定是指在设计时将值分配给变量,而后期绑定是指在运行时将值分配给变量。
有人可以定义两种绑定类型并进行比较吗?
我不断听到有关早期和晚期绑定的信息,但我不了解它们是什么。我发现以下我不理解的解释:
早期绑定是指在设计时将值分配给变量,而后期绑定是指在运行时将值分配给变量。
有人可以定义两种绑定类型并进行比较吗?
Answers:
混乱中有两个主要概念:绑定和加载。它与DataBinding的概念混为一谈,DataBinding经常位于两者中间。考虑一下之后,我将再添加一个概念,以完成三连胜,即派遣。
后期绑定:类型是未知的,直到在运行时执行变量为止;通常通过分配,但是还有其他手段可以强制类型;动态类型的语言将其称为基本功能,但是许多静态类型的语言都有一些实现后期绑定的方法
通常使用[特殊]动态类型,自省/反射,标志和编译器选项来实现,或者通过借用和扩展动态分配来通过虚拟方法来实现
早期绑定:类型是在运行时执行变量之前已知的类型,通常通过静态的声明性方式
经常使用标准基本类型实现
静态调度:编译时已知的特定函数或子例程;它是明确的并与签名匹配
实现为静态功能;没有方法可以具有相同的签名
动态调度:在编译时不是特定的函数或子例程;在执行期间由上下文确定。有两种不同的“动态调度”方法,以哪种上下文信息用于选择适当的功能实现来区分。
在单个 [ dynamic ] 调度中,仅使用实例的类型来确定适当的函数实现。在静态类型的语言中,这实际上意味着意味着实例类型决定使用哪种方法实现,而与声明/分配变量时指示的引用类型无关。因为仅使用一种类型(对象实例的类型)来推断适当的实现,所以此方法称为“单调度”。
还有多个 [ dynamic ] 调度,其中输入参数类型也有助于确定要调用的函数实现。因为实例的类型和参数的类型等多种类型都会影响选择哪种方法实现,所以这种方法被称为“多种调度”。
实现为虚拟或抽象功能;其他线索包括重写,隐藏或阴影方法。
注意: 方法重载是否涉及动态调度是特定于语言的。 例如,在Java中,重载方法是静态分派的。
延迟加载:对象初始化策略,将值分配推迟到需要时使用;允许对象处于本质上有效但已知为不完整的状态,并等待直到需要数据后再加载它;通常对于加载大型数据集或等待外部资源特别有用
通常是通过有目的地在构造器或初始化调用期间不将集合或列表加载到复合对象中来实现的,直到某个下游调用者要求查看该集合的内容(例如get_value_at,get_all_as等)。变化包括加载有关集合的元信息(如大小或键),但忽略实际数据;还为某些运行时提供了一种机制,为开发人员提供了一个相当安全有效的单例实现方案
急切加载:对象初始化策略,可以立即执行所有值分配,以便在考虑自身处于有效状态之前,需要完成所有数据。
通常是通过尽快向复合对象提供所有已知数据来实现的,例如在构造函数调用或初始化期间
数据绑定:通常涉及在两个兼容的信息流之间创建活动链接或映射,以使对一个信息的更改反映回另一信息,反之亦然;为了兼容,它们通常必须具有通用的基本类型或接口
通常是为了在不同应用程序方面(例如,从视图模型到视图,从模型到控制器等)之间提供更清洁,一致的同步而进行的尝试,并讨论诸如源和目标,端点,绑定/解除绑定,更新和事件之类的概念on_bind,on_property_change,on_explicit,on_out_of_scope
编辑注释:最后的主要编辑内容提供了有关如何频繁发生的示例的说明。特定的代码示例完全取决于实现/运行时/平台
后期绑定是在运行时评估行为时。当您确实想根据程序运行时所拥有的信息来确定如何采取措施时,这很有必要。我认为最清楚的例子是虚函数机制,特别是在C ++中。
class A
{
public:
void f() {}
virtual void g() {}
};
class B : public A
{
void f() {}
virtual void g() {}
};
int main()
{
A* a = new B;
a->f();
a->g();
}
在此示例中,a->f()
将实际调用void A::f()
,因为它是早期(或静态)绑定的,因此程序在运行时认为它只是指向A
类型变量的指针,而a->g()
实际上将调用void B::g()
,因为编译器看到的g()
是虚拟的,会注入代码以查找在运行时调用正确函数的地址。
如果您熟悉函数指针,这将是一个示例。定义的功能可以说是早期绑定。反之,如果您使用功能指针,则表示其后期绑定。
int add(int x,int y)
{
return x+y;
}
int sub(int x,int y)
{
return x-y;
}
int main()
{
//get user choice
int(*fp)(int,int);
//if add
fp=add;
//else if sub
fp=sub;
cout<<fp(2,2);
}
这里的函数add和sub是函数(其地址在编译时链接器处绑定)
但是函数指针绑定较晚,因此fp可以根据用户选择[在运行时]调用add或sub。
早期和晚期绑定仅在类型的上下文中有意义,而在您描述它的方式中则没有意义。几乎所有现代语言都是在所有值具有固定类型的意义上键入的。当我们查看动态类型和静态类型的语言时,就会出现差异。在动态类型语言中,变量没有类型,因此它们可以引用任何类型的值,这意味着当您在某个变量所引用的对象上调用方法时,确定该调用是否有效的唯一方法是:查找对象的类,看看该方法是否实际存在。这允许一些很酷的事情,例如在运行时向类添加新方法,因为实际的方法查找将推迟到最后一刻。大多数人将这种情况称为后期约束。
在静态类型语言中,变量具有类型,并且一旦声明就不能引用任何非同一类型的值。严格来说,这不是真的,但让我们暂时假设。现在,如果您知道变量将仅引用特定类型的值,则没有理由找出方法调用在运行时是否有效,因为您可以在代码运行之前确定有效性。这称为早期绑定。
演示在红宝石中后期绑定的示例:
a = 1 # a is an integer at this point
a.succ # asking for its successor is valid
class A
def method_a
# some code
end
end
a = A.new
a.method_a # this is also valid
a.succ # this is not valid
class A # we can re-open the class and add a method
def succ
# some more code
end
end
a.succ # now this is valid
在所有类型都在运行时固定的Java之类的语言中,上述动作序列是不可能的。
除了给您一个学术定义之外,我将尝试使用VBA的真实示例向您展示一些差异:
早期绑定:
Dim x As FileSystemObject
Set x = New FileSystemObject
Debug.Print x.GetSpecialFolder(0)
这要求在设计时将引用设置为“ Microsoft Scripting Runtime”组件。这样做的好处是,当您输入错字FileSystemObject
或类似方法的名称时,在编译时便会收到一条错误消息GetSpecialFolder
。
后期绑定
Dim x As Object
Set x = CreateObject("Scripting.FileSystemObject")
Debug.Print x.GetSpecialFolder(0)
这不需要事先设置引用,实例创建和类型确定将仅在运行时发生。当您尝试调用不存在的方法时x
,编译器不会在编译时抱怨,这仅在执行特定行时才会导致运行时错误。
因此,后期绑定的缺点是您在这里没有任何强类型检查。但这也是优点-假设您有一个组件,其中存在多个版本,并且每个较新的版本都提供一些其他功能。(一个真实的示例是MS Office组件,例如Excel COM界面。)后期绑定使您可以编写可与所有版本一起使用的代码-您可以首先确定特定的组件版本,如果发现自己拥有仅提供较旧的版本,请避免执行不适用于该版本的函数调用。