dynamic和generic type完全是两码事儿,dynamic不能弥补generic type。
诸如操作符的问题,应该用强类型接口解决。像这种所谓用dynamic弥补generic type的做法就是滥用,就是应当避免的。
dynamic的主要意义在于他只是一个表达式,而这个表达式的行为可以被动态对象在运行时重新诠释。从这一点上来说,dynamic和IQueryable有点类似。
譬如说,data.A,如果data是一个静态类型,那么这个A的意义就是编译时就确定的,但是如果data是dynamic,那么你可以在运行时再来决定这个data.A到底代表一个什么含义,甚至于你可以让data.A在不同的表达式之中表现出不同的行为出来,这才是dynamic不可替代的优势。
譬如说dynamic可以轻易地实现当data.A是null的时候,data.A.Name也是null,而不会报错,这对于一些特定的场合,例如数据绑定,是非常有用的。同时也可以使得在data.A+i这样的表达式中,data.A的值看起来就和0一样。
判断dynamic是不是被滥用了,我觉得一个比较简单的办法就是,除非你重写了IDynamicMetaObjectProvider对象,否则就是滥用,dynamic的意义在于运行时重新诠释表达式的行为,而默认的dynamic行为就是反射的一个语法糖而已。通过重写IDynamicMetaObjectProvider来重新诠释表达式的行为,则应当是被鼓励的,正确使用dynamic的形式。
例如http://ASP.NET MVC中使用dynamic作为ViewData索引器的语法糖,http://JSON.NET中作为修改JObject对象修改的语法糖,而我的DataPithy开源项目中,也使用dynamic作为DataRow对象成员访问的语法糖,通过dynamic,使得这些对象的访问变得简单,而性能也基本没有损失。
========================================================
补充一些东西:
1、提问者的问题本质:C++的模板的一些高级用法能否用C#的dynamic弥补。
不能。本质上有很大的区别,C++的模板是编译时展开的,这样才带来了一些高级的用法,而且不必声明类型约束,在展开时如果发现无法编译的情形直接在编译期就会报错。
而C#的泛型是在运行时展开,所以需要声明类型约束,在编译后还能继续对未知类型展开,这些都是优势,但由于缺乏运算符约束,像T+T这种运算无法写出来。
使用dynamic本质上是把+的运算放到运行时再由CSharpBinaryOperationBinder来处理,这样一来性能有大数量级的损失。实现原理完全不一样,性能差异巨大,所以不能作为C++模板的替代方案。
2、如果是做一个类似脚本的计算引擎场景,则应当直接拼接表达式树,避免计算结果,而是在最后对表达式树编译再运行计算,比分步计算结果性能要好。或者直接用DLR来处理,去除中间过程中没有必要的类型转换。
3、动态语言的重构显然不如静态语言来的方便,但也不至于成为一场灾难。严格控制动态类型的应用范围,避免多层级跨模块的动态调用,配合文本搜索,也能做动态类型的重构。当然,利用动态类型的特性,也可以在动态类型层面做向下兼容,例如属性A改成了B,那么继续保留A并重定向到B即可。