论文部分内容阅读
摘要: 主要目的是研究.NET中dynamic的优缺点,然后进行测试,并对它的功能和性能进行科学地分析和评判,最后提出使用dynamic的合理化建议。
关键词: 动态;反射;对象;静态类型
中图分类号:TP311 文献标识码:A 文章编号:1671-7597(2011)1110032-02
1 dynamic的基本理论
C# 4.0之前,每当声明一个变量时,都有一个具体的类型与之对应。因为编译器在编译时会进行类型检查,一旦出现类型匹配失败,编译器就会报错。C# 4.0新增了dynamic关键字,解决了直到在运行时才进行类型确定的问题。
1.1 dynamic的概念
Dynamic是一个类型关键字,声明为dynamic的类型与编译时就确定的类型相比,最大的特点就是“动态类型”,它会运行时尝试调用方法,这些方法的存在与否不是在编译时检查的,而是在运行时确定,如果方法存在并且参数正确,会正常调用,否则会导致RuntimeBinder.RuntimeBinderException异常。
1.2 dynamic类型与静态类型的区别
静态类型是一个编译时类型,即在编译时就已经确定的类型,同样地,静态绑定是编译器基于可知类型之上的绑定。
dynamic类型是动态类型,它是一个运行时类型,即在运行时确定它的类型,而不是编译时类型,当编译器在编译时发现有动态类型,就把绑定交给运行时(DLR)处理[1]。类似地,动态绑定是DLR在运行时基于运行时类型的绑定。
“dynamic”类型使得C#具有了后期绑定的功能。任何直接声明为这种类型的变量,或者从函数中返回这种类型的值,都将自动地视为后期绑定。
这里,需要注意以下几点:
1)dynamic类型有static的部分属性,但是dynamic类型的对象会绕过编译器检查。
2)dynamic类型并没有跳过类型校验,只是延迟到了运行时。如果在运行时,检测到类型不兼容,照样会抛出异常。
3)dynamic是个关键字,可是正因为这个关键字,C#动态特性向前迈进了一大步。
1.3 dynamic和var的区别
var只是隐含类型声明符,并且只能用作局部变量,它其实仍然是强类型,只不过它代表的类型是编译器根据初始化时的右值推断而来,所以对这个变量仍然可以使用Visual Studio的智能提示,而dynamic在没有运行的时候,是不能确定类型的,所以不能使用智能提示。
使用var时,编译器会根据右值来推断出变量的类型,也就是说,使用var声明的变量终究还是编译时由编译器进行类型检查。而dynamic更强大,编译时编译器根本不用对它进行类型推断,而是到运行时才由DLR再进行推断。
var只能用于局部变量的定义,不能把类的属性定义成var,也不能把方法的返回值类型或者是参数类型定义成var,而对dynamic就没有这些限制,它甚至可以被赋值或赋值给任何类型并且不需显式的强制类型转换。
1.4 dynamic和object的区别
dynamic和object类型在类型替换上有些相似,但它们的区别是在编译时才会体现出现,编译时dynamic类型被假定为支持任何操作,因此在代码中对dynamic类型的任意调用在编译时都是合法的,而在运行时如果发现该对象不是期望的对象就会抛出一个运行时异常。这一点object类型根本做不到,因为使用object类型时,它可以指向任意数据类型的数据[2],但必须在编译前进行正确类型的显式转换,否则在编译时编译器就不让通过,而不可能等到运行时才进行类型匹配。
1.5 dynamic和Reflection的区别
dynamic的主要目的是在静态类型的语言中去访问动态语言创建的对象,正因为如此,它的部分目的与使用反射和object类型的功能重合了,但是引入dynamic的初衷绝对不是要去代替反射和object类型,它们的区别主要体现在以下几点:
1)dynamic对象不但可以被用来访问动态语言创建的对象,而且可以与动态语言进行互操作。而Reflection只是用来引用未知类型,能以受控方式查看加载的类型、方法和字段,能够动态创建和调用类型[3],但它无法把类暴露给其他语言,所以在交互性上,与dynamic有一定的差距。
2)dynamic可以简化代码,它的语法比Reflection简单,因为使用Reflection需要处理各种各样的类型。
3)dynamic对象因为有了方法访问的缓存,性能比使用Reflection有一定提升。而Reflection目前还没有对访问方法的缓存。
4)dynamic和反射很类似,但是dynamic在代码里就可以直接实现对未知类型对象的操作。
2 dynamic的功能
dynamic的功能可以概述为以下两部分:
1)动态访问对象:这是dynamic所要做的主要工作,我们可以在运行时“动态”访问对象成员,而不是编译时访问对象。
2)创建动态对象:我们可以在运行时定义对象的行为,包括增删对象成员等。
dynamic常用于以下几种情况:
1)与外部对象交互的情形
这是dynamic最常用的一种功能,外部对象主要包括COM、DLR、HTML DOM、XML等对象,比如,你不能确定这些对象的具体类型而仅仅知道它的一些属性、方法等,如果你需要在程序运行到这里时才执行这些方法,至于操作对象是什么,你可能并不关心。这个时候,静态类型无法帮你解决问题,因为它们是在编译时就已经决定了的,反射虽然能做到,但毕竟太麻烦,而且效率较低,此时使用dynamic就很方便,它用编译时类型检查缺失的代价来实现更简单的代码。
此外,在COM方法中经常接收各种类型参数并且通常会返回object类型值,开发人员需要进行类型转换以进行进一步操作。在.NET Framework 4中,如果使用/link开关编译程序,dynamic类型允许在COM调用时将object类型作为dynamic类型对待,这样可以避免类型转换。
2)自动生成反射代码的情形
如果不能确定对象的具体类型而仅仅知道它的一些属性、方法,比如Show(),那么用dynamic对象调用这个已知的方法Show()时,Visual Studio会帮我们生成反射的代码,用反射的方式去调用这个方法Show(),实质上就是帮我们实现自动反射了。
3)混合编程时与动态语言(如IronPython,IronRuby等)对象交互的情形
4)C# 4.0中动态创建对象的情形
3 dynamic的优点
dynamic的优点和主要有:
1)dynamic方便了类型间的转换,特别是方便了与其它类型之间的相互隐式转换。
2)dynamic只须要在运行时第一次确定好类型后,它会变成强类型,这样在后面的运行中,会大大提高效率,而且运用起来也比较方便,免去了烦人的拆箱、装箱工作。
3)dynamic可以和动态语言互操作,它提供了诸如IronPython和IronRuby等动态编程语言的实现[2],可以同时享受动态语言的灵活和.Net的强大。
4)dynamic可以支持自顶而下的开发方式,从顶层的架构开始搭起会很容易,调用不存在的方法(甚至这个类都根本不存在)编译也没有问题。
5)dynamic消除了混乱的反射代码并可以简化代码复杂度。
6)dynamic有方法访问的缓存,可以提升比反射更好的性能。
4 dynamic的缺点
dynamic的缺点如下:
1)当与强类型的类一起使用的时候可能会损害性能,因为在第一次运行时确定类型前,需要拆箱、装箱等操作,此外,由于是动态类型的不确定性,所以把所有可能的类型都列举了出来,然后通过在运行时再判断其中类型,这部分操作也消耗了不少性能。
2)无法使用编译器智能提示。
3)无法在编译时做静态类型检查,从而失去了编译器的保护。
4)跨程序集使用“dynamic”时,原有的访问权限受到影响。在B程序集中定义的匿名类型和private类型,因为它们都是private权限,因而A程序集是无法直接获取其成员信息的,也无法输出。解决方法是预先定义为public的数据类型,但这种解决方法被迫改变了原有的访问权限,从而影响了面向对象编程良好的封装性。
5)目前动态查找不支持扩展方法的调用,当然该功能可能会在C#后续的版本中提供。
6)匿名方法和Lambda表达式不能转换为dynamic,lambda表达式其实也不能转成object。因为lambda表达式会在上下文环境下要么被编译器解释成委托类型,要么被解释成表达式,但是如果上下文缺乏类型信息,编译器就会报错。
5 使用dynamic的合理化建议
由于dynamic具有上述明显的优点和缺点,所以必须对它进行合理地使用,否则就会降低程序的性能,甚至会带来问题,特别是对代码进行重构的时候,如果代码中存在对dynamic的滥用,将会造成巨大的麻烦,因此,在使用dynamic的时候,我们需要注意以下几点:
1)如果遇到与外部对象如COM、DLR、HTML DOM、XML等对象交互的情况下,dynamic就充分显示了它的优势,所以就应该使用dynamic。
2)如果对于程序的性能要求很高并且程序并没有与外部对象如COM、DLR、HTML DOM、XML等对象交互的情况下,不建议使用dynamic,因为此时使用dynamic会大大影响到程序的性能。
3)如果在程序中必须使用反射的话,dynamic由于使用了缓存,性能优于反射,而且使用dynamic的代码量比使用反射少的多,所以应该使用dynamic。
4)如果遇到与动态语言(如IronPython,IronRuby等)对象交互的情况下,dynamic也有一定的性能优势,而且代码量小了很多,因为此时调用别人定义的方法就象在代码中调用自己定义的方法一样,非常的简单,这是dynamic的另一神奇之处,至于别人定义的方法是如何实现的,我们根本不用管,因为DLR会自动完成的,所以此时应该使用dynamic。
5)使用dynamic的程序最好做一下单体测试,因为它可能带来更多的运行时错误。
6)由于dynamic本身也是一个类型,而且dynamic实现了implicit和explicit运算符,因此理论上任何可以使用CLR的类型的地方都可以用dynamic。
6 结束语
综上所述,鉴于dynamic的明显的优点和同样明显的缺点,请大家在综合考虑程序的性能、代码量、测试及需求的基础上,合理使用它。
参考文献:
[1]MSDN使用类型,2010.7.
[2]MSDN数据类型,2010.8.
[3]MSDN System.Reflection,2010.8.
作者简介:
刘仲博(1973-),男,94年获得陕西师大数学系理学学士学位,97年获得西北政法学院法律本科,06年获得西安电子科技大学计算机科学与技术工学硕士学位,现任宁夏职业技术学院宁夏广播电视大学讲师。
关键词: 动态;反射;对象;静态类型
中图分类号:TP311 文献标识码:A 文章编号:1671-7597(2011)1110032-02
1 dynamic的基本理论
C# 4.0之前,每当声明一个变量时,都有一个具体的类型与之对应。因为编译器在编译时会进行类型检查,一旦出现类型匹配失败,编译器就会报错。C# 4.0新增了dynamic关键字,解决了直到在运行时才进行类型确定的问题。
1.1 dynamic的概念
Dynamic是一个类型关键字,声明为dynamic的类型与编译时就确定的类型相比,最大的特点就是“动态类型”,它会运行时尝试调用方法,这些方法的存在与否不是在编译时检查的,而是在运行时确定,如果方法存在并且参数正确,会正常调用,否则会导致RuntimeBinder.RuntimeBinderException异常。
1.2 dynamic类型与静态类型的区别
静态类型是一个编译时类型,即在编译时就已经确定的类型,同样地,静态绑定是编译器基于可知类型之上的绑定。
dynamic类型是动态类型,它是一个运行时类型,即在运行时确定它的类型,而不是编译时类型,当编译器在编译时发现有动态类型,就把绑定交给运行时(DLR)处理[1]。类似地,动态绑定是DLR在运行时基于运行时类型的绑定。
“dynamic”类型使得C#具有了后期绑定的功能。任何直接声明为这种类型的变量,或者从函数中返回这种类型的值,都将自动地视为后期绑定。
这里,需要注意以下几点:
1)dynamic类型有static的部分属性,但是dynamic类型的对象会绕过编译器检查。
2)dynamic类型并没有跳过类型校验,只是延迟到了运行时。如果在运行时,检测到类型不兼容,照样会抛出异常。
3)dynamic是个关键字,可是正因为这个关键字,C#动态特性向前迈进了一大步。
1.3 dynamic和var的区别
var只是隐含类型声明符,并且只能用作局部变量,它其实仍然是强类型,只不过它代表的类型是编译器根据初始化时的右值推断而来,所以对这个变量仍然可以使用Visual Studio的智能提示,而dynamic在没有运行的时候,是不能确定类型的,所以不能使用智能提示。
使用var时,编译器会根据右值来推断出变量的类型,也就是说,使用var声明的变量终究还是编译时由编译器进行类型检查。而dynamic更强大,编译时编译器根本不用对它进行类型推断,而是到运行时才由DLR再进行推断。
var只能用于局部变量的定义,不能把类的属性定义成var,也不能把方法的返回值类型或者是参数类型定义成var,而对dynamic就没有这些限制,它甚至可以被赋值或赋值给任何类型并且不需显式的强制类型转换。
1.4 dynamic和object的区别
dynamic和object类型在类型替换上有些相似,但它们的区别是在编译时才会体现出现,编译时dynamic类型被假定为支持任何操作,因此在代码中对dynamic类型的任意调用在编译时都是合法的,而在运行时如果发现该对象不是期望的对象就会抛出一个运行时异常。这一点object类型根本做不到,因为使用object类型时,它可以指向任意数据类型的数据[2],但必须在编译前进行正确类型的显式转换,否则在编译时编译器就不让通过,而不可能等到运行时才进行类型匹配。
1.5 dynamic和Reflection的区别
dynamic的主要目的是在静态类型的语言中去访问动态语言创建的对象,正因为如此,它的部分目的与使用反射和object类型的功能重合了,但是引入dynamic的初衷绝对不是要去代替反射和object类型,它们的区别主要体现在以下几点:
1)dynamic对象不但可以被用来访问动态语言创建的对象,而且可以与动态语言进行互操作。而Reflection只是用来引用未知类型,能以受控方式查看加载的类型、方法和字段,能够动态创建和调用类型[3],但它无法把类暴露给其他语言,所以在交互性上,与dynamic有一定的差距。
2)dynamic可以简化代码,它的语法比Reflection简单,因为使用Reflection需要处理各种各样的类型。
3)dynamic对象因为有了方法访问的缓存,性能比使用Reflection有一定提升。而Reflection目前还没有对访问方法的缓存。
4)dynamic和反射很类似,但是dynamic在代码里就可以直接实现对未知类型对象的操作。
2 dynamic的功能
dynamic的功能可以概述为以下两部分:
1)动态访问对象:这是dynamic所要做的主要工作,我们可以在运行时“动态”访问对象成员,而不是编译时访问对象。
2)创建动态对象:我们可以在运行时定义对象的行为,包括增删对象成员等。
dynamic常用于以下几种情况:
1)与外部对象交互的情形
这是dynamic最常用的一种功能,外部对象主要包括COM、DLR、HTML DOM、XML等对象,比如,你不能确定这些对象的具体类型而仅仅知道它的一些属性、方法等,如果你需要在程序运行到这里时才执行这些方法,至于操作对象是什么,你可能并不关心。这个时候,静态类型无法帮你解决问题,因为它们是在编译时就已经决定了的,反射虽然能做到,但毕竟太麻烦,而且效率较低,此时使用dynamic就很方便,它用编译时类型检查缺失的代价来实现更简单的代码。
此外,在COM方法中经常接收各种类型参数并且通常会返回object类型值,开发人员需要进行类型转换以进行进一步操作。在.NET Framework 4中,如果使用/link开关编译程序,dynamic类型允许在COM调用时将object类型作为dynamic类型对待,这样可以避免类型转换。
2)自动生成反射代码的情形
如果不能确定对象的具体类型而仅仅知道它的一些属性、方法,比如Show(),那么用dynamic对象调用这个已知的方法Show()时,Visual Studio会帮我们生成反射的代码,用反射的方式去调用这个方法Show(),实质上就是帮我们实现自动反射了。
3)混合编程时与动态语言(如IronPython,IronRuby等)对象交互的情形
4)C# 4.0中动态创建对象的情形
3 dynamic的优点
dynamic的优点和主要有:
1)dynamic方便了类型间的转换,特别是方便了与其它类型之间的相互隐式转换。
2)dynamic只须要在运行时第一次确定好类型后,它会变成强类型,这样在后面的运行中,会大大提高效率,而且运用起来也比较方便,免去了烦人的拆箱、装箱工作。
3)dynamic可以和动态语言互操作,它提供了诸如IronPython和IronRuby等动态编程语言的实现[2],可以同时享受动态语言的灵活和.Net的强大。
4)dynamic可以支持自顶而下的开发方式,从顶层的架构开始搭起会很容易,调用不存在的方法(甚至这个类都根本不存在)编译也没有问题。
5)dynamic消除了混乱的反射代码并可以简化代码复杂度。
6)dynamic有方法访问的缓存,可以提升比反射更好的性能。
4 dynamic的缺点
dynamic的缺点如下:
1)当与强类型的类一起使用的时候可能会损害性能,因为在第一次运行时确定类型前,需要拆箱、装箱等操作,此外,由于是动态类型的不确定性,所以把所有可能的类型都列举了出来,然后通过在运行时再判断其中类型,这部分操作也消耗了不少性能。
2)无法使用编译器智能提示。
3)无法在编译时做静态类型检查,从而失去了编译器的保护。
4)跨程序集使用“dynamic”时,原有的访问权限受到影响。在B程序集中定义的匿名类型和private类型,因为它们都是private权限,因而A程序集是无法直接获取其成员信息的,也无法输出。解决方法是预先定义为public的数据类型,但这种解决方法被迫改变了原有的访问权限,从而影响了面向对象编程良好的封装性。
5)目前动态查找不支持扩展方法的调用,当然该功能可能会在C#后续的版本中提供。
6)匿名方法和Lambda表达式不能转换为dynamic,lambda表达式其实也不能转成object。因为lambda表达式会在上下文环境下要么被编译器解释成委托类型,要么被解释成表达式,但是如果上下文缺乏类型信息,编译器就会报错。
5 使用dynamic的合理化建议
由于dynamic具有上述明显的优点和缺点,所以必须对它进行合理地使用,否则就会降低程序的性能,甚至会带来问题,特别是对代码进行重构的时候,如果代码中存在对dynamic的滥用,将会造成巨大的麻烦,因此,在使用dynamic的时候,我们需要注意以下几点:
1)如果遇到与外部对象如COM、DLR、HTML DOM、XML等对象交互的情况下,dynamic就充分显示了它的优势,所以就应该使用dynamic。
2)如果对于程序的性能要求很高并且程序并没有与外部对象如COM、DLR、HTML DOM、XML等对象交互的情况下,不建议使用dynamic,因为此时使用dynamic会大大影响到程序的性能。
3)如果在程序中必须使用反射的话,dynamic由于使用了缓存,性能优于反射,而且使用dynamic的代码量比使用反射少的多,所以应该使用dynamic。
4)如果遇到与动态语言(如IronPython,IronRuby等)对象交互的情况下,dynamic也有一定的性能优势,而且代码量小了很多,因为此时调用别人定义的方法就象在代码中调用自己定义的方法一样,非常的简单,这是dynamic的另一神奇之处,至于别人定义的方法是如何实现的,我们根本不用管,因为DLR会自动完成的,所以此时应该使用dynamic。
5)使用dynamic的程序最好做一下单体测试,因为它可能带来更多的运行时错误。
6)由于dynamic本身也是一个类型,而且dynamic实现了implicit和explicit运算符,因此理论上任何可以使用CLR的类型的地方都可以用dynamic。
6 结束语
综上所述,鉴于dynamic的明显的优点和同样明显的缺点,请大家在综合考虑程序的性能、代码量、测试及需求的基础上,合理使用它。
参考文献:
[1]MSDN使用类型,2010.7.
[2]MSDN数据类型,2010.8.
[3]MSDN System.Reflection,2010.8.
作者简介:
刘仲博(1973-),男,94年获得陕西师大数学系理学学士学位,97年获得西北政法学院法律本科,06年获得西安电子科技大学计算机科学与技术工学硕士学位,现任宁夏职业技术学院宁夏广播电视大学讲师。