dynamic跟反射有关系?

陆敏技的《编写高质量代码 改善C#程序的157个建议》书中建议15  使用dynamic来简化发射实现,看后很是不解   ,原文如下

———————————————————————————————————原文————————————————————————————————————————

dynamic是Framework 4.0的新特性。dynamic的出现让C#具有了弱语言类型的特性。编译器在编译的时候不再对类型进行检查,编译器默认dynamic对象支持开发者想要的任何特性。比如,即使你对GetDynamicObject方法返回的对象一无所知,也可以像如下这样进行代码的调用,编译器不会报错:

 

  • dynamic dynamicObject =GetDynamicObject(); 
  • Console.WriteLine(dynamicObject.Name); 
  • Console.WriteLine(dynamicObject.SampleMethod());

 

  当然,如果运行时dynamicObject不包含指定的这些特性(如上文中带返回值的方法SampleMethod),运行时程序会抛出一个RuntimeBinderException异常:

  “System.Dynamic.ExpandoObject”未包含“SampleMethod”的定义。

  注意 有人会将var这个关键字与dynamic进行比较。实际上,var和dynamic完全是两个概念,根本不应该放在一起比较。var实际上是编译期抛给我们的“语法糖”,一旦被编译,编译期会自动匹配var 变量的实际类型,并用实际类型来替换该变量的声明,这看上去就好像我们在编码的时候是用实际类型进行声明的。而dynamic被编译后,实际是一个object类型,只不过编译器会对dynamic类型进行特殊处理,让它在编译期间不进行任何的类型检查,而是将类型检查放到了运行期。

  这从Visual Studio的编辑器窗口就能看出来。以var声明的变量支持“智能感知”,因为Visual Studio能推断出var类型的实际类型;而以dynamic声明的变量却不支持“智能感知”,因为编译器对其运行期的类型一无所知。对dynamic变量使用“智能感知”,会提示“此操作将在运行时解析”。

  利用dynamic的这个特性,可以简化C#中的反射语法。在dynamic出现之前,假设存在类,代码如下所示:

 

  • public class DynamicSample 
  •     public string Name { get; set; } 
  •     public int Add(int a, int b) 
  •     { 
  •         return a + b; 
  •     } 
  • }

 

  我们这样使用反射,调用方代码如下所示:

 

  • DynamicSample dynamicSample =new DynamicSample(); 
  • var addMethod =typeof(DynamicSample).GetMethod("Add"); 
  • int re = (int)addMethod.Invoke(dynamicSample, new object[] { 1, 2 });

 

  在使用dynamic后,代码看上去更简洁了,并且在可控的范围内减少了一次拆箱的机会,代码如下所示:

 

  • dynamic dynamicSample2 =new DynamicSample(); 
  • int re2 =dynamicSample2.Add(1, 2);

 

  我们可能会对这样的简化不以为然,毕竟代码看起来并没有减少多少,但是,如果考虑到效率兼优美两个特性,那么dynamic的优势就显现出来了。如果对上面的代码执行1000000次,如下所示:

 

  • int times =1000000
  • DynamicSample reflectSample =new DynamicSample(); 
  • var addMethod =typeof(DynamicSample).GetMethod("Add"); 
  • Stopwatch watch1 =Stopwatch.StartNew(); 
  • for (var i =0; i<times; i++) 
  •     addMethod.Invoke(reflectSample, new object[] { 1, 2 }); 
  • Console.WriteLine(string.Format("反射耗时:{0} 毫秒", 
  •     watch1.ElapsedMilliseconds)); 
  • dynamic dynamicSample =new DynamicSample(); 
  • Stopwatch watch2 =Stopwatch.StartNew(); 
  • for (int i =0; i<times; i++) 
  •     dynamicSample.Add(1, 2); 
  • Console.WriteLine(string.Format("dynamic耗时:{0} 毫秒", 
  •     watch2.ElapsedMilliseconds));

输出为:

 

  • 反射耗时:2575 毫秒 
  • dynamic耗时:76 毫秒
 

  可以看到,没有优化的反射实现,上面这个循环上的执行效率大大低于dynamic实现的效果。如果对反射实现进行优化,代码如下所示:

 

  • DynamicSample reflectSampleBetter =new DynamicSample(); 
  • var addMethod2 =typeof(DynamicSample).GetMethod("Add"); 
  • var delg = (Func<DynamicSample, int, int, int>)Delegate.CreateDelegate(typeof( 
  •     Func<DynamicSample, int, int, int>), addMethod2); 
  • Stopwatch watch3 =Stopwatch.StartNew(); 
  • for (var i =0; i<times; i++) 
  •     delg(reflectSampleBetter, 1, 2); 
  • Console.WriteLine(string.Format("优化的反射耗时:{0} 毫秒", 
  •     watch3.ElapsedMilliseconds));

  输出为:

 

优化的反射耗时:12 毫秒

  可以看到,优化后的反射实现,其效率和dynamic在一个数量级上。可是它带来了效率,却牺牲了代码的整洁度,这种实现在我看来是得不偿失的。所以,现在有了dynamic类型,建议大家:

  始终使用dynamic来简化反射实现。

 

 

———————————————————————————————————原文————————————————————————————————————————
 
      这个例子,给出了所谓的用反射和dynamic二种方式实现Add方法的耗时比较,反射的方式确实是用了反射,通过字符串“Add”反射调用了Add方法,但是所谓的dynamic方式,是直接调用了Add方法,试问在不知道方法名为“Add”的情况下,你如何去用反射去调用该方法, int re2 = dynamicSample2 .Add(1, 2);因此这句话没有完全实现了反射的功能,还不如直接new一个DynaSampledynamicSample =new DynamicSample();  然后调用add方法。因此,本文后面所测试的执行效率的对比也没有找到本质原因。
      结论:该建议是坑!!
 
 
————————————————————————————————————————————————————————————————————————————
 
时隔一年,再回来看这篇文章,发现是我自己2了,向陆老师道歉!!                   2014年12月9日
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值