什么是反射、反射可以做些什么

2016-6-21 | 发布者:DotNet

(点击上方蓝字,可快速关注我们)


来源:农码一生

链接:http://www.cnblogs.com/zhaopei/p/reflection.html


什么是反射,反射能干嘛?


反射是:指程序可以访问、检测和修改它本身状态或行为的一种能力
反射是一种能力,所以给的定义就是说明了它能干嘛。


我们平时用反射主要做:


  • 获取类型的相关信息


  • 动态调用方法


  • 动态构造对象


  • 从程序集中获得类型。


获取类型的相关信息


反射的核心Type类,Type对象提供的属性和方法可以获取对象的一切信息,如:方法、字段、属性、事件…等等。


我们获取已加载程序集中类型的Type对象的几种方法:(以StringBuilder 类型为例)


  1. 直接使用typeof操作符 Type T1 = typeof(StringBuilder);


  2. 通过类型实例 Type T2 = new StringBuilder().GetType();


  3. 通过Type类的静态方法 Type T3 = Type.GetType(“System.IO.Stream”);


不管使用那种,我们最终得到的结果都是一样的。


那么我们通过Type又能得到些什么信息呢?


获取类型本身信息(命名空间名、全名、是否是抽象、是否是类、、、等等)


var T1 = typeof(StringBuilder);                      

Console.WriteLine("命名空间名称:" + T1.Namespace);

Console.WriteLine("直接基类型:" + T1.BaseType);

Console.WriteLine("全名:" + T1.FullName);

Console.WriteLine("是抽象类型:" + T1.IsAbstract);

Console.WriteLine("是类:" + T1.IsClass);

//.....等等



获取类型成员信息(通过Tyep中的方法GetMembers)


Type T1 = typeof(TClass);

var Mets = T1.GetMembers();//获取Type对象的所有公有成员          

foreach (var m in Mets)

{

    Console.WriteLine("【" + m.MemberType.ToString()+ "】:" + m.Name);

    // m.MemberType 是成员类型

}



MemberType所能包含的成员类型有哪些呢?如:(可以自己可以F12进去看看)



注意:其中MemberInfo的属性DeclaringType返回的是这个属性定义的类型,而ReflectedType返回的是获取这个属性的对象类型。


如:


Type T2 = typeof(TClass);

var Mets = T2.GetMembers();//获取所有公共成员(返回值是MemberInfo类型集合)

foreach (var m in Mets)

{

    if (m.Name=="Equals")

    {

        Console.WriteLine("【" + m.MemberType.ToString() + "】:" + m.Name);

        // m.MemberType 是成员类型

 

        // m.DeclaringType;//获取申明该成员的类

        // m.ReflectedType;//获取用于获取 MemberInfo 的此实例的类对象。

 

    }

}


T2中的Equals,我们知道这个方式是在Objec中定义的,在TClass中调用的,所以:



我们发现获取Type对象的成员大多都是以 isxxx、Getxxx、Getxxxs格式的。


isxxx格式的基本上都是判断是否是某类型。


Getxxx和Getxxxs都是放回某类型和某类型集合。其中主要的类型有:


//FieldInfo封装了关于字段的所有信息   (通过Tyep对象的GetFields或GetField方法)

 

//PropertyInfo类型,封装了类型的属性信息;(通过Type对象的GetProperties或GetProperty方法)

 

//ConstructorInfo类型,封装了类型的构造函数信息; (..........)

 

//MethodInfo类型,封装了类型的方法信息;  (........)

 

//MemberInfo类型,封装了类型的所有公共成员;(**就是我们上面说的GetMembers方法**)

 

//EventInfo类型,封装了类型的事件信息;(.......)

 

//ParameterInfo类型,封装了方法和构造函数的参数信息;(........)


它们都在 System.Reflection 命名空间下,其每个isxxx、Getxxx、Getxxxs的细节实例用法就不一一演示了。和上面的GetMembers用法区别不大。


动态调用方法


首先定义个类:


public class TClass

{

    public void fun(string str)

    {

        Console.WriteLine("我是fun方法,我被调用了。" + str);

    }

    public void fun2()

    {

        Console.WriteLine("我是fun2方法,我被调用了。");

    }

 

    public static void fun3()

    {

        Console.WriteLine("我是fun3静态方法,我被调用了");

    }

}


调用方式一(使用InvokeMember调用方法)


调用带参实例方法fun


Type T1 = typeof(TClass);

T1.InvokeMember("fun", BindingFlags.InvokeMethod, null, new TClass(), new string[] { "test" });



调用无参实例方法fun2


Type T1 = typeof(TClass);

T1.InvokeMember("fun2", BindingFlags.InvokeMethod, null, new TClass(), null);


调用静态方法


Type T1 = typeof(TClass);

T1.InvokeMember("fun3", BindingFlags.InvokeMethod, null, T1, null);


我们发现了一个问题当我们调用实例方法的时候需要传实例对象过去。(有人会说,都实例对象了,我还要你动态掉调用个屁啊。有种情况,在我们实例了对象后,仍不确定应该调用那个方法时可以只有使用。然后有人有说了,那如果实例对象我也不确定呢?那我们下面会分析连实例对象也给动态了。那接着完下看吧。)


我们来说下这几个参数的意思吧。


第一个:要被动态调用的方法名。


第二个:是一个枚举,表示是调用一个方法


第三个:是Binder,传的是null,使用默认值。


第四个:传如实例对象(调用实例方法时)或者Type对象(调用静态方法时)。


第五个:要传给被调用发的参数数组。


调用方式二(使用MethodInfo.Invoke调用方法)


Type T1 = typeof(TClass);

T1.GetMethod("fun", BindingFlags.Instance | BindingFlags.Public).Invoke(new TClass(), new string[] { "testfun1" });

T1.GetMethod("fun2", BindingFlags.Instance | BindingFlags.Public).Invoke(new TClass(), null);

T1.GetMethod("fun3", BindingFlags.Static | BindingFlags.Public).Invoke(T1, null);




使用其实和上面的方式一区别不大。


真正的全动态调用


上面的两种方式,在编写代码的时候总是要先确定了已知的对象名和方法名。那么我们在不知道对象和方法名的时候是否也可以调用呢?答案是肯定的,实现如下:


Console.WriteLine("请输入对象类名");

string className = Console.ReadLine();

Console.WriteLine("请输入要执行的方法名");

 

string funName = Console.ReadLine();

Type T1 = Type.GetType(className);

 

ConstructorInfo ci = T1.GetConstructors()[0]; //获取构造函数

var obj = ci.Invoke(null);//实例化构造函数

 

T1.InvokeMember(funName, BindingFlags.InvokeMethod, null, obj, null);



当然,这个代码只能只是fun2,因为上面的传参写死了。(你也可以自己稍微修改下,就可以执行fun、fun2、fun3了)


效果如下:(对象名和方法名都是手动输入的)



动态构造对象


我们先定义一个对象:


public class TClass

{

    public TClass()

    {

        Console.WriteLine("构造函数被执行了。。");

    }

    public TClass(string str)

    {

        Console.WriteLine("有参构造函数被执行了。。" + str);

    }        

}


动态构造对象


//动态构造对象,方式一

Assembly asm = Assembly.GetExecutingAssembly();

TClass obj = (TClass)asm.CreateInstance("net.tclass", true);//true:不区分大小写

 

//动态构造对象,方式二

ObjectHandle handler = Activator.CreateInstance(null, " net.TClass");//null:当前程序集

obj = (TClass)handler.Unwrap();

 

//动态构造对象,方式三(构造有参构造函数)

Assembly asm2 = Assembly.GetExecutingAssembly();

obj = (TClass)asm2.CreateInstance("net.tclass", true, BindingFlags.Default, null, new string[] { "test" }, null, null);//true:不区分大小写


执行效果图:



获取和修改属性


var obj = new TClass();

obj.name = "张三";

Type type = typeof(TClass);

//获取属性

var Name = type.InvokeMember("name", BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance, null,

                     obj, new object[] { }) as string;

Console.WriteLine(obj.name);

//设置属性

type.InvokeMember("name", BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance, null,

                      obj, new object[] { "新属性(李四)" });

Console.WriteLine(obj.name);



从程序集中获得类型


取得当前代码所在程序集(使用GetExecutingAssembly)


Assembly ass = Assembly.GetExecutingAssembly();

Console.WriteLine("当前所在程序集名:"+ass.ManifestModule.Name);

Console.WriteLine("当前所在程序集路径:"+ass.Location);



通过反射加载程序集并创建程序中的类型对象


从程序集中获得类型,这个应该是我们平时用得比较多。如我们所谓的依赖注入和控制反转(这个主题将在下篇博文进行分析)就用到了通过反射从程序集中获取类型。


首先我们还是看看怎么从程序集中获得类型吧。我们可以使用Assembly类型提供的静态方法LoadFrom()或Load(),如:


Assembly asm = Assembly.LoadFrom("Demo.dll");

Assembly asm = Assembly.Load("Demo");


区别:


Assembly asm = Assembly.LoadFrom("net.exe");//需要加后缀,可以指定路径,如下面的

Assembly asm1 = Assembly.LoadFrom(@"C:1文件5SvnBlogsCodeBlogsBlogs.WebbinBlogs.BLL.dll");

 

Assembly asm2 = Assembly.Load("Blogs.BLL");//无需加后缀,不可以指定路径

//Assembly asm3 = Assembly.Load(@"C:1文件5SvnBlogsCodeBlogsBlogs.WebbinBlogs.BLL");//这里会报错

//使用Load可以加载当前程序bin目录行下的程序集或者系统程序集

 

//这里TClass可以是一个接口,那么可以在外面的dll任意实现了。  

TClass obj = (TClass)asm2.CreateInstance("net.tclass", true);//true:不区分大小写

obj.fun();//***调用动态加载的dll中的方法***


这样带来的功能是非常强大的。如 我们在没有引用程序集的情况下,也可以使用到程序外的程序集。我们还可以根据不同情况引用不同的程序集。我们甚至还可以通过配置文件来直接配置代码运行时应该加载哪个dll,运行哪个dll中的哪个实现方法。(下篇在讲依赖注入的时候会讲到,同学们继续关注哦~)

从上所知,反射不是某一个概率,而是一类操作的统称。或者说是某些能力的统称。 感觉不好回答反射到底是什么,只能说反射能干什么。它能动态创建对象、动态调用对象方法、动态读取和设置属性和字段、它能动态加载程序外的dll。总的感觉就是大多数都是跟“动态”扯上了关系。


【今日微信公号推荐↓】


更多推荐请看值得关注的技术和设计公众号


其中推荐了包括技术设计极客 和 IT相亲相关的热门公众号。技术涵盖:Python、Web前端、Java、安卓、iOS、PHP、C/C++、.NET、Linux、数据库、运维、大数据、算法、IT职场等。点击《值得关注的技术和设计公众号》,发现精彩!



热门文章

更多

潮人帮

这些韩国娃娃都这么潮!连宋仲基的三观都快被颠...

这些韩国娃娃都这么潮!连宋仲基的三观都快被颠...

听说大家今晚又要开始追老公了!小橱决定退出这场“战斗”顺便再送你们张结婚照,成全你们的心意,让你跟老公更近点......再近点......(自己去P,随意美颜~就是别动老公的脸啊......)姑娘们千万别怀疑我的动机...
明星衣橱 • 
好想要 羽毛婚纱这么美,婚礼不穿准后悔~

好想要 羽毛婚纱这么美,婚礼不穿准后悔~

虽然每年流行的婚纱款式都大同小异,无非是蕾丝白纱堆一块,可在我们看来,那些轮廓相似的大裙子却各有各的玄机。尤其当下婚纱潮流里极为盛行的羽毛元素,它大气华美的视觉效果几乎让每个披上身的新娘都看上去光...
时尚新娘 • 
无论年龄她们都是一脸清纯,秘籍在这里,韩国最...

无论年龄她们都是一脸清纯,秘籍在这里,韩国最...

韩国两大“自然无添加”女神总是以清纯温柔的面貌示人,在小美心中最温柔的女人就是淡妆微笑,安静生活,就像下面这二位一样。近日,李英爱为韩国《JLOOK》品牌拍摄了一组时尚大片,今年已45岁的她,面对镜头,...
美妆汇 • 
宋慧乔的养颜秘诀~

宋慧乔的养颜秘诀~

导语《太阳的后裔》一开播,就发现了一件恐怖的事情!那就是宋慧乔今年已经35岁了,从出道到现在也已经整整20年了!当别人老态尽显时,却唯独乔妹越长越嫩!“宋慧乔的颜值巅峰永远在下一步剧”!别羡慕,今天小...
HOT韩妆护肤小妙... • 
搭配 夏季想要精致简约搭就戳这里

搭配 夏季想要精致简约搭就戳这里

试问一姑娘你喜欢什么风格呢她肯定会说越简单越好是的当今我们流行的是简约美多数姑娘被这种简约美所迷倒▼半袖篇夏季清爽 干净的style它们来帮你撑场面▼衬衫篇衬衫当然是职场妹妹不可少的也可以扮成高冷范儿这...
欧美街拍 • 
加载更多