大多数脚本语言使用运行时绑定,大多数编译语言使用编译时绑定。C# 既然是一种编译语言,那就是一种编译时绑定的语言,但它对动态绑定支持良好,其具有反射类型的功能。在这篇文章中,我们将探讨理论和示例在C#中的运行时绑定和编译时绑定。
1、编译时绑定
当在编译期间就需要找到目标方法时,绑定被称为早期绑定、编译时绑定或静态类型绑定(将在编译期间创建调用该方法的代码)。如果所需的方法不存在,则在编译期间会发出错误。
在呼叫期间是否有额外的步骤来查找该方法无关紧要。
这意味着,无论该方法是否为虚拟方法,都仍会尽早考虑绑定。
2、运行时绑定
C#中的运行时绑定或后期绑定是通过反射
实现的。
运行时绑定意味着在运行时查找目标方法。通常,通过该方法的文本名称
查找它。如果该方法不存在,则程序将在运行时崩溃或进入某种异常处理方案。
例如,下面的代码使用反射来定位和执行两种方法,一种存在,另一种不存在。由于激活器返回的对象已转换为ILateBoundSample,因此编译器知道该方法NotExistingMethod不存在。
另一方面,编译器知道带有的方法RandomMethod,但这并不意味着它知道加载的程序集中发生了什么。如果加载的类型是ILateBoundSample该方法的不同版本的实现,则该方法可能不存在。
但是,在下面的示例中,我们使用反射而不进行任何转换,因此编译器永远不知道发生了什么,只是假设我们知道我们在做什么。以下代码的最后一部分将产生运行时错误:
//Find the assembly path
var assemblyPath = Path.Combine( Path.GetDirectoryName( Assembly.GetExecutingAssembly().Location ), "LateBindingHelloWorld.Module.dll");
//Load the assembly
var assembly = Assembly.LoadFile(assemblyPath);
//Load all types that implement the ILateBoundSample interface
var types = assembly.GetTypes().Where(p => typeof(ILateBoundSample).IsAssignableFrom(p)).ToList();
if (types.Any())
{
//Create an instance of that class
var lateBoundSample = Activator.CreateInstance(types.First());
//Execute the method
lateBoundSample.HelloWorld();
}
3、运行时绑定性能
运行时绑定通常会影响性能,因为运行时绑定需要在运行时进行查找,因此,开发人员必须选择哪个阶段会受到绑定的影响更大;通常是选择编译时间。
尽管存在运行时延迟,但是在大多数情况下并没有那么大:
对于正常功能,编译器可以计算出它在内存中的数字位置;当调用该函数时,它可以生成一条指令以在该地址处调用该函数。
对于具有任何虚拟方法的对象,编译器将生成一个数组,其中包含虚拟方法的地址。该数组称为VMT或虚拟方法表。
另外,再次在编译期间,编译器还将为该对象生成一个隐藏成员,其中包含先前创建的VMT的地址。
调用虚拟函数时,编译器将确定VMT中适当方法的位置。然后它将生成代码以查找对象VMT并在此位置调用虚拟方法。
对虚拟方法进行的查找已得到高度优化,因此在运行时将非常快速地进行查找。
但这并不意味着没有任何开销,因此,当性能至关重要时,可以选择早期绑定
(或至少在启动过程中进行后期绑定缓存)。
4、dynamic是什么
在C#4中引入了许多内容,包括dynamic类型。
一般将dynamic视为后期绑定,但事实并非如此!
通常,编程语言是动态类型的或强类型的,而C#(众所周知)是强类型的。
动态类型(dynamic)是C# 4中引入的一种补充,它通过在运行时解析信息而与两个世界结合在一起,而与类型无关。这根本没有绑定,因为它从不绑定到类型
。
更多内容,请参考微软大大的文档。
5、小结
编程是一种练习,练习的越多,手 … ,越熟!
关注楼主,不迷路,小手一抖,一键三连!