在 C# 中反射手艺运用普遍,至于什么是反射.........你假如不相识的话,请看下段申明,否则请跳过下段。广告一下:喜欢我文章的朋侪请关注一下我的blog,这也有助于进步本人写作的动力。
反射:当你背对一个玉人或帅哥却不能转头仔细观察研讨时(纯属虚构,若有偶合、纯属相同),一面小镜子就可以满足你的需求。在 C# 编程过程当中也常常碰到相似的状况:有一个他人写的 dll 类库你想运用却没顺序文档资料......此时经由过程 C# Runtime 供应的功用,你能够把该 dll 类库加载到你的顺序中,并细细研讨 dll 的每一部分内容,这就是 C# 中的反射。
个人以为反射最凸起的长处或存在的合理性:在不修正顺序原码的状况下,完成顺序功用的动态调解(Runtime动态对象建立)
示例:
interface IRun { void Run(); } class Person : IRun { public void Run() { Console.WriteLine("走,去LOL啊!"); } } class Car : IRun { public void Run() { Console.WriteLine("呜..........."); } } class Program { static void Main(string[] args) { IRun e = new Person(); e.Run(); Console.ReadLine(); } }
假如将上面的Run功用并不一定是由Person来实行,偶然须如果Car偶然须要Person。罕见的处理方案是增添 if 等推断构造,以下:
static void Main(string[] args) { Console.WriteLine("请输入:Car或Person"); string type = Console.ReadLine(); IRun e = null; if ("Car" == type) { e = new Car(); }else if("Person" == type) { e = new Person(); } if(null != e) e.Run(); Console.ReadLine(); }
这类构造确是处理了如今的需求,但并不硬朗。跟着 IRun 接口完成、相干类的继续的增添,上面的推断构造也会飞速增进。面向对象编程、设想形式均遵照的一大准绳就是封装变动,所以上面的顺序没法很好的应对变化。在此我们并不触及 “设想形式的” 的学问,因而下面的示例代码只为简化上面的顺序、并未锐意套用设想形式相干学问。以下:
static void Main(string[] args) { Console.WriteLine("请输入:Car或Person"); string type = Console.ReadLine(); string classPath = String.Format("namespace.{0}", type); IRun e = Activator.CreateInstance(null, classPath).Unwrap() as IRun; if(null != e) e.Run(); Console.ReadLine(); }
经由上面的修正,顺序可自行依据用户的输入,经由过程Activator.CreateInstance建立 IRun 的实例,顺序此处不会再随 IRun 的完成者增加这类题目的影响而发生变化。上面的这类长处就是经由过程反射取得的,也是我所以为的“反射存在的合理性”。
Activator、Assembly 完成反射体式格局建立对象
C#中反射体式格局建立对象能够经由过程 Activator.CreateInstance(静态)和 Assembly.CreateInstance(非静态)来完成,个中Assembly.CreateInstance 内部挪用的还是Activator.CreateInstance。
依据要动态建立的范例对象是不是处于当前顺序集当中,可将反射建立对象分为:建立顺序集内的范例对象与建立顺序集外的范例对象。
建立顺序集内的范例对象
private static void ReflectionIRun1(string className) { string classPath = String.Format("namespace.{0}", className); //参数 null ,指出所要建立范例对象位于当前顺序集 var handler = Activator.CreateInstance(null, classPath); IRun e = (IRun)handler.Unwrap(); Console.WriteLine(e.Run()); } private static void ReflectionIRun2(string className) { string classPath = String.Format("namespace.{0}", className); //typeof(IRun).Assembly 猎取 IRun 范例地点的顺序集 object obj = typeof(IRun).Assembly.CreateInstance(null, classPath); IRun e = (IRun)obj; Console.WriteLine(e.Run()); }
建立顺序集外的范例对象
项目中增添一个 类库 (另一个顺序集),以下图:
增添一个 Boss 类,以下:
namespace Lib { public class Boss { private string name = "老大"; public string Name{ get {return name;} } public string Talk() { return "你们都被开除了......"; } //老板不会算账,老是多付钱,所以很有自知之明的将Payfor设为private,防备外部职员挪用 private int Payfor(int total) { return total + 10; } } }
猎取 一个 Boss 对象前,起首增添对 Lib 的援用,猎取示例以下:
private static void ReflectionBoss1() { string classPath ="Lib.Boss"; //"Lib" 参数指明要加载的顺序集(即要建立的对象范例在哪一个顺序集合定义) var handler = Activator.CreateInstance("Lib", classPath); Boss b = handler.Unwrap() as Boss; Console.WriteLine(b.Talk()); } private static void ReflectionBoss2() { string classPath ="Lib.Boss"; //Assembly.Load("Lib") 加载的顺序集(即要建立的对象范例在哪一个顺序集合定义) var assembly = Assembly.Load("Lib"); Boss b = (Boss)assembly.CreateInstance(classPath); Console.WriteLine(b.Talk()); }
关于反射时CLR怎样查找并定位要加载的顺序集,请参考MSDN中关于反射相干的学问。
反射接见字段、挪用要领(属性)
反射除能够帮我们动态建立对象外,还可帮我们动态接见对象的要领(属性)或字段,因 C# 版本差别具体要领会有变动或扩大,更深入内容请参考MSDN。下面仅作简朴示例(规范用法)。
给老板更名,示例:
private static void ReflectionBoss1() { string classPath = "Lib.Boss"; //"Lib" 参数指明要加载的顺序集(即要建立的对象范例在哪一个顺序集合定义) var handler = Activator.CreateInstance("Lib", classPath); Boss b = handler.Unwrap() as Boss; //症结代码 FieldInfo f = b.GetType().GetField("name", BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance); f.SetValue(b, "小二"); Console.WriteLine("{0}:{1}", b.Name, b.Talk()); }
输出:
让老板付钱:
private static void ReflectionBoss1() { string classPath = "Lib.Boss"; //"Lib" 参数指明要加载的顺序集(即要建立的对象范例在哪一个顺序集合定义) var handler = Activator.CreateInstance("Lib", classPath); Boss b = handler.Unwrap() as Boss; //症结代码 MethodInfo method = b.GetType().GetMethod("Payfor", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance); object money = method.Invoke(b, new object[] { 10 }); Console.WriteLine("DW039:老大给我报销10元钱车资......"); Console.WriteLine("{0}:.....,算不清了,给你这些吧。",b.Name); Console.WriteLine("DW039:......"); Console.WriteLine("{0}:{1}", b.Name,money); Console.WriteLine("DW039:老大你真棒!"); }
输出:
dynamic 与 反射 双剑合璧
由于反射是运转时的范例操纵,所以在编程时面对范例不确定的题目。依据上一篇《C# 匿名对象(匿名范例)、var、动态范例 dynamic》讲得 dynamic 动态范例连系我们编写的反射顺序,能够大大优化顺序逻辑(接见受庇护级别限定的代码不在此范围内)。
上面代码的优化:
private static void ReflectionBoss1() { string classPath ="Lib.Boss"; var handler = Activator.CreateInstance("Lib", classPath); dynamic b = handler.Unwrap(); Console.WriteLine(b.Talk()); } private static void ReflectionBoss2() { string classPath ="Lib.Boss"; var assembly = Assembly.Load("Lib"); dynamic b = assembly.CreateInstance(classPath); Console.WriteLine(b.Talk()); }
经由过程 dynamic 动态范例对象 b 来挪用反射取得对象的属性、要领可直接挪用,从而省去了频仍的范例转换操纵。
反射罕见运用场景
运用场景我印象最深入的是 MS Petshop 示例,从SQL Server 数据库切换到 oracle 数据库时反射取得差别的数据接见层。然我现实项目中从未碰到过半途切换数据库的状况,其他运用场景基础相似上面的示例。假如朋侪你发明更多的运用场景,请赋予补充,3ks。
反射的优瑕玷
长处:反射使顺序更天真
瑕玷:反射运转速率相对较慢
至于反射比拟一般顺序慢,我没有举行过测试也不打算举行。现实状况是:Ms首倡运用 dynamic、Mvc盛行、Ms对CLR不停优化、机械机能的提拔,所以你在开辟中无需过量斟酌反射的机能题目。假如你写的顺序运转速率涌现了瓶颈(应起首确保本身顺序写的合理),研讨一下数据库优化、数据缓存、web缓存、负载平衡等手艺我以为更现实一些。
以上就是C#中关于反射和dynamic最好组合的示例分享的细致内容,更多请关注ki4网别的相干文章!