在 C# 中反射技术应用广泛,至于什么是反射.........你如果不了解的话,请看下段说明,否则请跳过下段。广告一下:喜欢我文章的朋友请关注一下我的blog,这也有助于提高本人写作的动力。

反射:当你背对一个美女或帅哥却不能回头仔细观察研究时(纯属虚构,如有巧合、纯属雷同),一面小镜子就能满足你的需求。在 C# 编程过程中也经常遇到类似的情况:有一个别人写的 dll 类库你想使用却没程序文档资料......此时通过 C# Runtime 提供的功能,你可以把该 dll 类库加载到你的程序中,并细细研究 dll 的每一部分内容,这就是 C# 中的反射。

个人认为反射最突出的优点或存在的合理性:在不修改程序原码的情况下,实现程序功能的动态调整(Runtime动态对象创建

示例:

  1. interface IRun {
  2. void Run();
  3. }
  4. class Person : IRun
  5. {
  6. public void Run()
  7. {
  8. Console.WriteLine("走,去LOL啊!");
  9. }
  10. }
  11. class Car : IRun
  12. {
  13. public void Run()
  14. {
  15. Console.WriteLine("呜...........");
  16. }
  17. }
  18.  
  19. class Program
  20. {
  21. static void Main(string[] args)
  22. {
  23. IRun e = new Person();
  24. e.Run();
  25. Console.ReadLine();
  26. }
  27. }

如果将上面的Run功能并不一定是由Person来执行,有时需要是Car有时需要Person。常见的解决方案是添加 if 等判断结构,如下:

  1. static void Main(string[] args)
  2. {
  3. Console.WriteLine("请输入:Car或Person");
  4. string type = Console.ReadLine();
  5. IRun e = null;
  6. if ("Car" == type)
  7. {
  8. e = new Car();
  9. }else if("Person" == type)
  10. {
  11. e = new Person();
  12. }
  13. if(null != e)
  14. e.Run();
  15.  
  16. Console.ReadLine();
  17. }

这种结构确是解决了现在的需求,但并不健壮。随着 IRun 接口实现、相关类的继承的增加,上面的判断结构也会飞速增长。面向对象编程、设计模式均遵循的一大原则就是封装变换,所以上面的程序无法很好的应对变化。在此我们并不涉及 “设计模式的” 的知识,因此下面的示例代码只为简化上面的程序、并未刻意套用设计模式相关知识。如下:

  1. static void Main(string[] args)
  2. {
  3. Console.WriteLine("请输入:Car或Person");
  4. string type = Console.ReadLine();
  5. string classPath = String.Format("namespace.{0}", type);
  6. IRun e = Activator.CreateInstance(null, classPath).Unwrap() as IRun;
  7.  
  8. if(null != e)
  9. e.Run();
  10.  
  11. Console.ReadLine();
  12. }

经过上面的修改,程序可自行根据用户的输入,通过Activator.CreateInstance创建 IRun 的实例,程序此处不会再随 IRun 的实现者增多这种问题的影响而发生变化。上面的这种优点就是通过反射得到的,也是我所认为的“反射存在的合理性”。

Activator、Assembly 实现反射方式创建对象

C#中反射方式创建对象可以通过 Activator.CreateInstance(静态)和 Assembly.CreateInstance(非静态)来实现,其中Assembly.CreateInstance 内部调用的仍是Activator.CreateInstance。

根据要动态创建的类型对象是否处于当前程序集之中,可将反射创建对象分为:创建程序集内的类型对象与创建程序集外的类型对象。

创建程序集内的类型对象

  1. private static void ReflectionIRun1(string className)
  2. {
  3. string classPath = String.Format("namespace.{0}", className);
  4. //参数 null ,指出所要创建类型对象位于当前程序集
  5. var handler = Activator.CreateInstance(null, classPath);
  6. IRun e = (IRun)handler.Unwrap();
  7. Console.WriteLine(e.Run());
  8. }
  9. private static void ReflectionIRun2(string className)
  10. {
  11. string classPath = String.Format("namespace.{0}", className);
  12. //typeof(IRun).Assembly 获取 IRun 类型所在的程序集
  13. object obj = typeof(IRun).Assembly.CreateInstance(null, classPath);
  14. IRun e = (IRun)obj;
  15. Console.WriteLine(e.Run());
  16. }

创建程序集外的类型对象

项目中增加一个 类库 (另一个程序集),如下图:

添加一个 Boss 类,如下:

  1. namespace Lib
  2. {
  3. public class Boss
  4. {
  5. private string name = "老大";
  6.  
  7. public string Name{
  8. get {return name;}
  9. }
  10. public string Talk()
  11. {
  12. return "你们都被开除了......";
  13. }
  14. //老板不会算账,总是多付钱,所以很有自知之明的将Payfor设为private,防止外部人员调用
  15. private int Payfor(int total)
  16. {
  17. return total + ;
  18. }
  19. }
  20. }

获取 一个 Boss 对象前,首先添加对 Lib 的引用,获取示例如下:

  1. private static void ReflectionBoss1()
  2. {
  3. string classPath ="Lib.Boss";
  4. //"Lib" 参数指明要加载的程序集(即要创建的对象类型在哪个程序集中定义)
  5. var handler = Activator.CreateInstance("Lib", classPath);
  6. Boss b = handler.Unwrap() as Boss;
  7. Console.WriteLine(b.Talk());
  8. }
  9. private static void ReflectionBoss2()
  10. {
  11. string classPath ="Lib.Boss";
  12. //Assembly.Load("Lib") 加载的程序集(即要创建的对象类型在哪个程序集中定义)
  13. var assembly = Assembly.Load("Lib");
  14. Boss b = (Boss)assembly.CreateInstance(classPath);
  15. Console.WriteLine(b.Talk());
  16. }

关于反射时CLR如何查找并定位要加载的程序集,请参考MSDN中关于反射相关的知识。

反射访问字段、调用方法(属性

反射除可以帮我们动态创建对象外,还可帮我们动态访问对象的方法(属性)或字段,因 C# 版本不同具体方法会有变更或扩展,更深入内容请参考MSDN。下面仅作简单示例(标准用法)。

给老板改名,示例:

  1. private static void ReflectionBoss1()
  2. {
  3. string classPath = "Lib.Boss";
  4. //"Lib" 参数指明要加载的程序集(即要创建的对象类型在哪个程序集中定义)
  5. var handler = Activator.CreateInstance("Lib", classPath);
  6. Boss b = handler.Unwrap() as Boss;
  7. //关键代码
  8. FieldInfo f = b.GetType().GetField("name", BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance);
  9. f.SetValue(b, "小二");
  10.  
  11. Console.WriteLine("{0}:{1}", b.Name, b.Talk());
  12. }

输出:

让老板付钱:

  1. private static void ReflectionBoss1()
  2. {
  3. string classPath = "Lib.Boss";
  4. //"Lib" 参数指明要加载的程序集(即要创建的对象类型在哪个程序集中定义)
  5. var handler = Activator.CreateInstance("Lib", classPath);
  6. Boss b = handler.Unwrap() as Boss;
  7. //关键代码
  8. MethodInfo method = b.GetType().GetMethod("Payfor", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance);
  9. object money = method.Invoke(b, new object[] { });
  10. Console.WriteLine("DW039:老大给我报销10元钱车费......");
  11. Console.WriteLine("{0}:.....,算不清了,给你这些吧。",b.Name);
  12. Console.WriteLine("DW039:......");
  13. Console.WriteLine("{0}:{1}", b.Name,money);
  14. Console.WriteLine("DW039:老大你真棒!");
  15. }

输出:

dynamic 与 反射 双剑合璧

因为反射是运行时的类型操作,所以在编程时面临类型不确定的问题。根据上一篇《C# 匿名对象(匿名类型)、var、动态类型 dynamic》讲得 dynamic 动态类型结合我们编写的反射程序,可以大大优化程序逻辑(访问受保护级别限制的代码不在此范围内)。

上面代码的优化:

  1. private static void ReflectionBoss1()
  2. {
  3. string classPath ="Lib.Boss";
  4. var handler = Activator.CreateInstance("Lib", classPath);
  5. dynamic b = handler.Unwrap();
  6. Console.WriteLine(b.Talk());
  7. }
  8. private static void ReflectionBoss2()
  9. {
  10. string classPath ="Lib.Boss";
  11. var assembly = Assembly.Load("Lib");
  12. dynamic b = assembly.CreateInstance(classPath);
  13. Console.WriteLine(b.Talk());
  14. }

通过 dynamic 动态类型对象 b 来调用反射得到对象的属性、方法可直接调用,从而省去了频繁的类型转换操作。

反射常见应用场景

应用场景我印象最深刻的是 MS Petshop 示例,从SQL Server 数据库切换到 oracle 数据库时反射获得不同的数据访问层。然我实际项目中从未遇到过中途切换数据库的情况,其他应用场景基本类似上面的示例。如果朋友你发现更多的应用场景,请给予补充,3ks。

反射的优缺点

优点:反射使程序更灵活

缺点:反射运行速度相对较慢

至于反射相比普通程序慢,我没有进行过测试也不打算进行。现实情况是:Ms提倡使用 dynamic、Mvc流行、Ms对CLR不断优化、机器性能的提升,所以你在开发中无需过多考虑反射的性能问题。如果你写的程序运行速度出现了瓶颈(应首先确保自己程序写的合理),研究一下数据库优化、数据缓存、web缓存、负载均衡等技术我认为更实际一些。

请放心大胆的使用反射技术吧,朋友!

随后我将写一系列关于C# 异步编程的文章,感兴趣的朋友请点下面的 “关注我” ,谢谢。

C# 反射、与dynamic最佳组合的更多相关文章

  1. C# 反射与dynamic最佳组合

    在 C# 中反射技术应用广泛,至于什么是反射.........你如果不了解的话,请看下段说明,否则请跳过下段.广告一下:喜欢我文章的朋友请关注一下我的blog,这也有助于提高本人写作的动力. 反射:当 ...

  2. java 搭建新项目,最佳组合:spring boot + mybatis generator

    java 搭建新项目,最佳组合:spring boot + mybatis generator

  3. 反射与dynamic

    反射 var a = Assembly.GetExecutingAssembly(); Type type = a.GetType("CLRTest.ReflectClass"); ...

  4. 【插件】WordPress缓存最佳组合:DB Cache Reloaded Fix + Hyper Cache

    DB Cache Reloaded Fix是一个出色的WordPress数据库缓存插件,可以大大减少对数据库的请求次数. Hyper Cache 是非常小巧但很强大的WordPress缓存插件,设置简 ...

  5. Windows 平台做 Python 开发的最佳组合

    在 Windows 上怎样做 Python 开发?是像大神那样使用纯文本编辑器,还是用更加完善的 IDE?到底是用自带的命令行工具,还是需要装新的 Terminal?本文将带你了解如何利用微软官方维护 ...

  6. NOIP模拟赛 最佳组合

    题目描述 Description \(Bzeroth\) 大陆最终还是覆灭了,所以你需要为地灾军团服务了. 地灾军团军师黑袍不擅长写题面,所以你只需要看简化版的题意即可. 给定 \(3\) 个长度均为 ...

  7. Visual Studio 调试技巧:10 篇热文汇总

    本文精选了 DotNet  2017年11月份的10篇热门文章.其中有技术分享.技术资源. 注:以下文章,点击标题即可阅读 <Visual Studio的调试技巧 > 调试技巧是衡量程序员 ...

  8. 编写高质量代码改善程序的157个建议:使用Dynamic来简化反射的实现

    最近有时间看点书了,把157个建议在重新看一遍,代码都调试一遍.当我看到第15个建议的时候有些出入,就记录下来,欢迎大家来探讨. 第十五条建议是,使用dynamic简化反射的使用,没有说明具体的条件. ...

  9. 编写高质量代码改善C#程序的157个建议——建议15: 使用dynamic来简化反射实现

    建议15: 使用dynamic来简化反射实现 dynamic是Framework 4.0的新特性.dynamic的出现让C#具有了弱语言类型的特性.编译器在编译的时候不再对类型进行检查,编译器默认dy ...

随机推荐

  1. ngin隐藏版本号

    Nginx默认是显示版本号的,如:   这样就给人家看到你的服务器nginx版本,这样暴露出来的版本号就容易变成攻击者可利用的信息.所以,从安全的角度来说,隐藏版本号会相对安全些! 配置如下: 修改n ...

  2. python函数(2):函数进阶

    昨天说了函数的一些最基本的定义,今天我们继续研究函数.今天主要研究的是函数的命名空间.作用域.函数名的本质.闭包等等 预习: 1.写函数,用户传入修改的文件名,与要修改的内容,执行函数,完成整个文件的 ...

  3. 【SpringMVC】使用SpringMVC进行上传文件!

    写在前面: 之前在上传文件的时候,使用的是commons-file-upload这个插件,非常方便,能控制每个文件的大小,总共大小,缓存,以及支持多个文件的同时上传,但是写一次上传文件的后台代码量太大 ...

  4. (转)Centos搭建FTP服务器

    场景:ftp服务器对于在Linux服务器上进行文件操作太方便,在安装软件时候,大的软件也可以先上传再进行安装! 1 搭建FTP服务器 1.1 检查vsftpd 查看是否已经安装vsftpd rpm - ...

  5. Yii框架用ajax提交表单时候报错Bad Request (#400): Unable to verify your data submission.

    提交表单报400错误,提示 "您提交的数据无法验证"原来是csrf验证的问题,因为表单是自己写的,在Yii框架中,为了防止csrf攻击,对post的表单数据封装了CSRF令牌验证. ...

  6. FPGA计算3行同列数据之和

    实验:FPGA计算3行同列数据之和 实验要求:PC机通过串口发送3行数据(一行有56个数据,3行共有56*3=168个数据)给FPGA,FPGA计算3行同一列数据的和,并将结果通过串口返回给上位机. ...

  7. openstack中使用linux_bridge实现vxlan网络

    openstack环境: 1 版本:ocata 2 系统:ubuntu16.04.2 3 控制节点 1个 + 计算节点 1个 4 控制节点网卡为ens33,ip = 172.171.5.200 ens ...

  8. Android学习笔记- Fragment实例 底部导航栏的实现

    1.要实现的效果图以及工程目录结构: 先看看效果图吧: 接着看看我们的工程的目录结构: 2.实现流程: Step 1:写下底部选项的一些资源文件 我们从图上可以看到,我们底部的每一项点击的时候都有不同 ...

  9. 腾讯 AlloyCrop 1.0 发布

    写在前面 AlloyCrop 这个项目是8个月前发布的,作为AlloyFinger 的典型案例,发布之后被BAT等其他公司广泛使用.但是发布之后,有两个问题一直没有抽出时间去解决: 裁剪图像的分辨率太 ...

  10. 使用 Router 实现的模块化,如何优雅的回到主页面

    使用 Router 实现的模块化,如何优雅的回到主页面 版权声明: 本账号发布文章均来自公众号,承香墨影(cxmyDev),版权归承香墨影所有. 未经允许,不得转载. 一.前言 现在越来越多的 App ...