搜文章
推荐 原创 视频 Java开发 iOS开发 前端开发 JavaScript开发 Android开发 PHP开发 数据库 开发工具 Python开发 Kotlin开发 Ruby开发 .NET开发 服务器运维 开放平台 架构师 大数据 云计算 人工智能 开发语言 其它开发
Lambda在线 > DotNet > C# 反射、与dynamic最佳组合

C# 反射、与dynamic最佳组合

DotNet 2017-11-29


来源:DW039

cnblogs.com/dw039/p/7476011.html


在 C# 中反射技术应用广泛,至于什么是反射.........你如果不了解的话,请看下段说明,否则请跳过下段。


反射:当你背对一个美女或帅哥却不能回头仔细观察研究时(纯属虚构,如有巧合、纯属雷同),一面小镜子就能满足你的需求。


在 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());

}


输出:


C# 反射、与dynamic最佳组合


让老板付钱:


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:老大你真棒!");

}


输出:


C# 反射、与dynamic最佳组合


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缓存、负载均衡等技术我认为更实际一些。


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


看完本文有收获?请转发分享给更多人

关注「DotNet」,提升.Net技能 


淘口令复制以下红色内容,再打开手淘即可购买


范品社,使用¥极客T恤¥抢先预览(长按复制整段文案,打开手机淘宝即可进入活动内容)


版权声明:本站内容全部来自于腾讯微信公众号,属第三方自助推荐收录。《C# 反射、与dynamic最佳组合》的版权归原作者「DotNet」所有,文章言论观点不代表Lambda在线的观点, Lambda在线不承担任何法律责任。如需删除可联系QQ:516101458

文章来源: 阅读原文

相关阅读

关注DotNet微信公众号

DotNet微信公众号:iDotNet

DotNet

手机扫描上方二维码即可关注DotNet微信公众号

DotNet最新文章

精品公众号随机推荐