zoukankan      html  css  js  c++  java
  • 编写高质量代码改善C#程序的157个建议——建议15: 使用dynamic来简化反射实现

    建议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();  //为了代码简洁简写了,没有写反射 Assembly.Load(AssemblyPath).CreateInstance(classNamespace),详细反射代码见文章结尾,实际开发中是要用反射生成实例的
        var addMethod = typeof(DynamicSample).GetMethod("Add");  
        int re = (int)addMethod.Invoke(dynamicSample, new object[] { 1, 2 }); 

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

        dynamic dynamicSample2 = new DynamicSample(); //为了代码简洁简写了,没有写反射 Assembly.Load(AssemblyPath).CreateInstance(classNamespace),详细反射代码见文章结尾,实际开发中是要用反射生成实例的 
        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来简化反射实现。

    转自:《编写高质量代码改善C#程序的157个建议》陆敏技

    反射代码:

    namespace ReflectionTest
    {
        public class DynamicSample
        {
            public string Name { get; set; }
    
            public int Add(int a, int b)
            {
                return a + b;
            }
        } 
    }
    static void Main(string[] args)
    {
    
        string AssemblyPath = "ReflectionTest";//可以通过读取配置文件来设置值
        string classNamespace = "ReflectionTest.DynamicSample";
    
        //原来反射方式
        Object dynamicSample = Assembly.Load(AssemblyPath).CreateInstance(classNamespace);
        Type type = dynamicSample.GetType();
        var addMethod = type.GetMethod("Add");
        int re = (int)addMethod.Invoke(dynamicSample, new object[] { 1, 2 });
        Console.WriteLine(re);//输出3
    
        //dynamic反射方式
        dynamic dynamicSample2 = Assembly.Load(AssemblyPath).CreateInstance(classNamespace);
        int re2 = dynamicSample2.Add(1, 2);//Add为动态表达式,将在运行时解析,这里是没有智能提示的
        Console.WriteLine(re2);//输出3
    
        Console.Read();
    }
  • 相关阅读:
    备忘录方法与动态规划比较
    struct大小
    位域
    cocos2d-x获得系统的语言
    游戏中的心理学:利用归因错误让玩家更爱你的游戏
    武侠游戏核心追求点
    将“Cocos2dx-截屏并设置图片尺寸 ”中cocos2d-x代码转换为2.2的代码
    Cocos2dx-截屏并设置图片尺寸
    信息图:iOS 7开发者需要知道的事
    批处理学习 二
  • 原文地址:https://www.cnblogs.com/jesselzj/p/4725852.html
Copyright © 2011-2022 走看看