• C#秘密武器之反射——替换反射


    反射虽然有时很有必要,但是应用反射的代码大多“复杂难懂”、“性能不高”,因此我们可以找寻在一些场景下替换反射的方法。此处也只是一些栗子,更多巧妙的应用还是自己以后亲自查查~

    先来看看一个使用普通反射完成的简单Demo:首先创建一个Person类,这个类非常简单,一个Name的public属性,一个_age的私有变量。

    复制代码
    public class Person
    {
          private readonly int _age;
          public Person(int age)
          {
              _age = age;
          }
          public string Name { get; set; }
          public bool GuessAge(int age)
          {
              return _age == age;
          }
    }
    复制代码

    接下来,看看常规的使用reflection获取一个person对象公开属性,私有变量同时调用对象的方法:

    复制代码
    var type = p.GetType();
    var property = type.GetProperty("Name");//根据名称获取类型属性
    Console.WriteLine(property.GetValue(p).ToString());//获取对象的属性值
    var field = type.GetField("_age", BindingFlags.NonPublic | BindingFlags.Instance);//获取私有变量_age, 后面的BindingFlags非常重要,否则默认是不能够取到private的东西
    Console.WriteLine(field.GetValue(p));
    var guessResult = type.InvokeMember("GuessAge", BindingFlags.InvokeMethod, null, p, new object[] { 20 });//调用对象的方法
    Console.WriteLine(guessResult);
    复制代码

    1. 使用PrivateObject

    什么是PrivateObject, PrivateObject是微软在单元测试中引入的,本意是方便我们写单元测试的时候,对于私有变量,方法,能够非常简单方便的调用。但是这也不妨碍我们在开发代码中使用。使用PrivateObject只需要引用Microft.VisualStudio.QualityTools.UnitTestFramework

    image

    接下来看看,如何使用PrivateObject来实现:

    复制代码
    var privateObject = new PrivateObject(p);
    Console.WriteLine(privateObject.GetProperty("Name"));
    Console.WriteLine(privateObject.GetField("_age"));
    
    Console.WriteLine(privateObject.Invoke("GuessAge", new object[] { 20 }));
    复制代码

    上面的代码和使用Reflection的效果完全一样。是不是觉得整个世界都清净许多。在代码的可读性上面,比Reflection好不少。

    2. 使用dynamic

    使用动态类型,可以非常简单方便的访问对象的属性的方法,比如上面的代码,如果我用dynamic实现:

    dynamic person = p;
    Console.WriteLine(person.Name);
    //Console.WriteLine(person._age);
    Console.WriteLine(person.GuessAge(20));

    使用dynamic的前提是,你在写代码的时候,就需要知道该对象的确切的属性名字和方法名,不能作为参数传递。而上面的Refelction和PrivateObject是可以的。
    使用dynamic还有一个缺点,就是无法访问到对象的私有成员。这也是注释掉_age输出的原因。

    真实的使用场景是,可以在不需要定义接口的情况下,实现通用的代码。比如Person有个Start属性, Car也有个Start属性,有个功能是需要为由Start的东西,显示的时候,都要带个星星的图标,这个时候,使用dynamic,就能够写出同时支持Person和Car的方法。

    3. 使用Exposed

    使用dynamic不能访问私有成员的问题,在Exposed里得到完全解决,从名字(翻译成暴露)也能看出来,它就是干这个的。

    var exposedObj = Exposed.From(p);
    Console.WriteLine(exposedObj.Name);
    Console.WriteLine(exposedObj._age);
    Console.WriteLine(exposedObj.GuessAge(20));

    Exposed是第三方开源的,项目地址是https://github.com/Cognifide/ExposedObject,也可以在nuget中下载到。

    4. 大杀器Clay

    看到上面的“废话”,动态语言的爱好者只会冷笑一下,丑陋的静态编译语言,这些东西在动态语言里面,“这都不是事”。好吧,我承认,但是看完了Clay,也许能改变你的看法。

    复制代码
    dynamic New = new ClayFactory();
    var person = New.Person().Name("Louis")._age(30);
    person.GuessAge = new Func<int, bool>(x => x == person._age);
    Console.WriteLine(person.Name);
    Console.WriteLine(person._age);
    Console.WriteLine(person.GuessAge()(20));
    复制代码

    5. 使用委托来优化反射

    namespace ReflectionOptimization
    {
       public sealed class TestObject
       {
            public int Add(int a, int b)
            {
             // 简单演示
              return a + b;
            }
       }
    }

    使用委托来优化反射:

    // 委托
    public delegate int AddMethod(int a, int b);
    
    // 实现
    var obj = new TestObject();
    var objType = obj.GetType();
    var add = objType.GetMethod("Add");
    var d = (AddMethod)Delegate.CreateDelegate(typeof(AddMethod), obj, add);
    
    for (var i = 0; i < 10000; i++) 
    d(a, b);

    使用委托优化反射之后,其性能与直接调用相差无几,保持在同一个数量级之内,对性能要求极度苛刻时推荐此方案;显式委托(Delegate)和匿名委托(Func)性能差异非常不明显,但显式委托的性能还是好一点! 

  • 相关阅读:
    POJ 2749
    POJ 3422
    POJ 3621
    SQLSERVER 2005 重新安装过程中的疑难解决
    可遇不可求的Question之MySqlClient访问字段返回System.Byte[]篇
    可遇不可求的Question之odbc驱动无法加载
    可遇不可求的BUG之采用MYSQL odbc 3.51访问数据库返回值缺失
    可遇不可求的Bug之Convert.Int32(<未定义的值>)等于0
    可遇不可求的Question之数据库操作超时篇
    可遇不可求的Question之数据库 'tempdb' 的日志已满。
  • 原文地址:https://www.cnblogs.com/WeiGe/p/4202644.html
Copyright © 2020-2023  润新知