慎用NET4.0新特性——dynamic

qldsrx 2012-08-03 05:29:14
这个关键字的出现,可以让编译器在运行的时候检查数据类型,大大方便了编程,但是效率十分堪忧。
下面给一段测试代码说明其效率问题:

private static void Main(string[] args)
{
object a = 1;//装箱操作,a的原始类型是int
int b = (int) a;//需要拆箱操作才能对b赋值
int c = (dynamic)a;//dynamic也可以完成拆箱动作
int d = (dynamic)a;//重复一遍看看IL代码是否有优化
Console.ReadLine();
}

下面看产生的IL代码:

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 3
.locals init (
[0] object a,
[1] int32 b,
[2] int32 c,
[3] int32 d)
L_0000: nop
L_0001: ldc.i4.1
L_0002: box int32
L_0007: stloc.0
L_0008: ldloc.0
L_0009: unbox.any int32
L_000e: stloc.1
L_000f: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite, object, int32>> ConsoleApplication1.Program/<Main>o__SiteContainer0::<>p__Site1
L_0014: brtrue.s L_003c
L_0016: ldc.i4.0
L_0017: ldtoken int32
L_001c: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_0021: ldtoken ConsoleApplication1.Program
L_0026: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_002b: call class [System.Core]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::Convert(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, class [mscorlib]System.Type, class [mscorlib]System.Type)
L_0030: call class [System.Core]System.Runtime.CompilerServices.CallSite`1<!0> [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite, object, int32>>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder)
L_0035: stsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite, object, int32>> ConsoleApplication1.Program/<Main>o__SiteContainer0::<>p__Site1
L_003a: br.s L_003c
L_003c: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite, object, int32>> ConsoleApplication1.Program/<Main>o__SiteContainer0::<>p__Site1
L_0041: ldfld !0 [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite, object, int32>>::Target
L_0046: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite, object, int32>> ConsoleApplication1.Program/<Main>o__SiteContainer0::<>p__Site1
L_004b: ldloc.0
L_004c: callvirt instance !2 [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite, object, int32>::Invoke(!0, !1)
L_0051: stloc.2
L_0052: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite, object, int32>> ConsoleApplication1.Program/<Main>o__SiteContainer0::<>p__Site2
L_0057: brtrue.s L_007f
L_0059: ldc.i4.0
L_005a: ldtoken int32
L_005f: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_0064: ldtoken ConsoleApplication1.Program
L_0069: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_006e: call class [System.Core]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::Convert(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, class [mscorlib]System.Type, class [mscorlib]System.Type)
L_0073: call class [System.Core]System.Runtime.CompilerServices.CallSite`1<!0> [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite, object, int32>>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder)
L_0078: stsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite, object, int32>> ConsoleApplication1.Program/<Main>o__SiteContainer0::<>p__Site2
L_007d: br.s L_007f
L_007f: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite, object, int32>> ConsoleApplication1.Program/<Main>o__SiteContainer0::<>p__Site2
L_0084: ldfld !0 [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite, object, int32>>::Target
L_0089: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite, object, int32>> ConsoleApplication1.Program/<Main>o__SiteContainer0::<>p__Site2
L_008e: ldloc.0
L_008f: callvirt instance !2 [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite, object, int32>::Invoke(!0, !1)
L_0094: stloc.3
L_0095: call string [mscorlib]System.Console::ReadLine()
L_009a: pop
L_009b: ret
}

IL分析:
object a = 1;对应了IL代码的L_0001 —— L_0007
int b = (int) a;对应了IL代码的L_0008 —— L_000e
int c = (dynamic)a;对应了IL代码的L_000f —— L_0051
int d = (dynamic)a;对应了IL代码的L_0052 —— L_0094

可以看出,装箱、拆箱动作产生的IL代码只有3行而已,但是换成了一个看似简单的dynamic后,产生的IL代码却如此之多,而且它所加载的额外类型并不能重利用,第二次使用到dynamic进行类型转换时,又产生同样多的IL代码。可见dynamic的处理效率是多么的低下,因此新手们请尽量不要用它,特别是循环内部别用,减少使用次数。

有一种场合只能用它,就是通过Linq返回匿名类型时,如果这个返回的对象需要声明到全局,显然是不支持的,但通过全局定义一个dynamic类型的变量,就可以将一个匿名类型存储进去,跨函数作用域访问了。

如果你们还有印象的,应该记得我曾经发过一个有关数据库的高效操作类,因其原先就用到了dynamic且当时我对这个类型还有好奇感,故而也多处用了它,现实际用了下后发现,其加载速度很成问题,原因就是上面提及的,所以我将所有dynamic的地方都替换掉后,发现效率提高为原来的10倍以上。目前正在针对Oracle数据库的单独优化中,等优化成熟后再发布第二个修改版。
...全文
437 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
E次奥 2012-09-04
  • 打赏
  • 举报
回复
Linq 匿名类中使用过,蛮方便的!
宝_爸 2012-09-04
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 的回复:]

没用过这么高级的语法糖
[/Quote]

这个应该不是语法糖了。
showjim 2012-09-04
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 的回复:]

看过你的上一篇,也许自己包装一下Dictionary<string, object>就可以了
[/Quote]
当然用起来没那么方便。
还好我用的是代码生成,没有这问题。
showjim 2012-09-04
  • 打赏
  • 举报
回复
看过你的上一篇,也许自己包装一下Dictionary<string, object>就可以了
corn8888 2012-08-03
  • 打赏
  • 举报
回复
哦 学习了 支持楼主
机器人 2012-08-03
  • 打赏
  • 举报
回复
asp.net razor 引擎里也大量使用了dynamic。就是你说的那种场景。

guoxin52416 2012-08-03
  • 打赏
  • 举报
回复
真心看不懂
qldsrx 2012-08-03
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 的回复:]
有一种场合只能用它,就是通过Linq返回匿名类型时,如果这个返回的对象需要声明到全局,显然是不支持的,但通过全局定义一个dynamic类型的变量,就可以将一个匿名类型存储进去,跨函数作用域访问了。

为什么不用object呢?
[/Quote]
因为对于匿名类型,虽然可以通过object装箱传递,但是你如何拆箱?你根本没有该类型的预先定义存在,既然无法拆箱,就无法直接访问内部属性,这样就很麻烦,难道通过反射?此时dynamic就提供了便利,虽然编码时不知道类型,但是仍旧可以访问属性,具体属性是否存在的验证是在运行时才触发。
xiaoyao1212121 2012-08-03
  • 打赏
  • 举报
回复
哇....学习了,好材料!
threenewbee 2012-08-03
  • 打赏
  • 举报
回复
有一种场合只能用它,就是通过Linq返回匿名类型时,如果这个返回的对象需要声明到全局,显然是不支持的,但通过全局定义一个dynamic类型的变量,就可以将一个匿名类型存储进去,跨函数作用域访问了。

为什么不用object呢?
bwangel 2012-08-03
  • 打赏
  • 举报
回复
MSDN上已经说了, dynamic只是让你在调用COM控件时用的于一些不确定的object对象。给COM交互带来极大的便利。
没叫你有事没事就dynamic. 连一些基本的可以预测的也dynamic,这不是没事找抽吗?
SocketUpEx 2012-08-03
  • 打赏
  • 举报
回复
没用过这么高级的语法糖


110,546

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧