C# · 12月 20, 2021

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

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

反射是:

我们平时用反射主要做:

获取类型的相关信息动态调用方法动态构造对象从程序集中获得类型。获取类型的相关信息

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

我们获取已加载程序集中类型的Type对象的几种方法:

直接使用typeof操作符  T1 = (StringBuilder); 通过类型实例  T2 = StringBuilder().GetType(); 通过Type类的静态方法  T3 = Type.GetType(); 

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

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

获取类型本身信息() T1 = + + + + +

获取类型成员信息Type T1 = Mets = T1.GetMembers(); ( m + m.MemberType.ToString()+ +}

MemberType所能包含的成员类型有哪些呢?如:

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

如:

Type T2 = Mets = ( m (m.Name== + m.MemberType.ToString() + +

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

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

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

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

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

动态调用方法

首先定义个类:

fun( + </span><span style=”color: #0000ff;”&gt;public</span> <span style=”color: #0000ff;”&gt;static</span> <span style=”color: #0000ff;”&gt;void</span><span style=”color: #000000;”&gt; fun3(){ Console.WriteLine(</span><span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;我是fun3静态<a href=”https://www.jb51.cc/tag/fangfa/” target=”_blank” class=”keywords”>方法</a>,我被<a href=”https://www.jb51.cc/tag/diaoyong/” target=”_blank” class=”keywords”>调用</a>了</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;);}

}

调用方式一

调用带参实例方法fun

Type T1 = ,BindingFlags.InvokeMethod,, TClass(), [] { });

调用无参实例方法fun2

Type T1 = ,);

调用静态方法

Type T1 = ,T1,);

我们发现了一个问题当我们调用实例方法的时候需要传实例对象过去。()

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

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

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

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

第四个:传如实例对象()或者Type对象()。

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

调用方式二Type T1 = ,BindingFlags.Instance | BindingFlags.Public).Invoke( TClass(), [] { ,,BindingFlags.Static | BindingFlags.Public).Invoke(T1,);

 

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

真正的全动态调用

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

Console.WriteLine( className =<span style=”color: #0000ff;”>string funName =<span style=”color: #000000;”> Console.ReadLine();
Type T1 =<span style=”color: #000000;”> Type.GetType(className);

ConstructorInfo ci = T1.GetConstructors()[<span style=”color: #800080;”>0]; <span style=”color: #008000;”>//<span style=”color: #008000;”>获取构造函数
<span style=”color: #0000ff;”>var obj = ci.Invoke(<span style=”color: #0000ff;”>null);<span style=”color: #008000;”>//<span style=”color: #008000;”>实例化构造函数
<span style=”color: #000000;”>
T1.InvokeMember(funName,<span style=”color: #0000ff;”>null,obj,<span style=”color: #0000ff;”>null);

当然,这个代码只能只是fun2,因为上面的传参写死了。() 

效果如下:()

动态构造对象

我们先定义一个对象:

TClass( +

动态构造对象

Assembly asm == (TClass)asm.CreateInstance(,);<span style=”color: #008000;”>//<span style=”color: #008000;”>动态构造对象,方式二
ObjectHandle handler = Activator.CreateInstance(<span style=”color: #0000ff;”>null,<span style=”color: #800000;”>”<span style=”color: #800000;”> net.TClass<span style=”color: #800000;”>”);<span style=”color: #008000;”>//<span style=”color: #008000;”>null:当前程序集
obj =<span style=”color: #000000;”> (TClass)handler.Unwrap();

<span style=”color: #008000;”>//<span style=”color: #008000;”>动态构造对象,方式三(构造有参构造函数)
Assembly asm2 =<span style=”color: #000000;”> Assembly.GetExecutingAssembly();
obj = (TClass)asm2.CreateInstance(<span style=”color: #800000;”>”<span style=”color: #800000;”>net.tclass<span style=”color: #800000;”>”,<span style=”color: #0000ff;”>true,BindingFlags.Default,<span style=”color: #0000ff;”>new <span style=”color: #0000ff;”>string[] { <span style=”color: #800000;”>”<span style=”color: #800000;”>test<span style=”color: #800000;”>” },<span style=”color: #0000ff;”>null);<span style=”color: #008000;”>//<span style=”color: #008000;”>true:不区分大小写

执行效果图:

获取和修改属性 obj = = = Name = type.InvokeMember(,BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance, [] { }) type.InvokeMember(,BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance, [] { PropertyInfo[] pros = type.GetProperties(—);//

从程序集中获得类型取得当前代码所在程序集Assembly ass = Assembly.GetExecutingAssembly();Console.WriteLine(“当前所在程序集名:”+ass.ManifestModule.Name);Console.WriteLine(“当前所在程序集路径:”+ass.Location);

 

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

从程序集中获得类型,这个应该是我们平时用得比较多。如我们所谓的依赖注入和控制反转就用到了通过反射从程序集中获取类型。

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

Assembly asm = Assembly.LoadFrom(= Assembly.Load();

区别:

Assembly asm = Assembly.LoadFrom();Assembly asm1 = Assembly.LoadFrom(Assembly asm2 = Assembly.Load(<span style=”color: #800000;”>”<span style=”color: #800000;”>Blogs.BLL<span style=”color: #800000;”>”);<span style=”color: #008000;”>//<span style=”color: #008000;”>无需加后缀,不可以指定路径
<span style=”color: #008000;”>//<span style=”color: #008000;”>Assembly asm3 = Assembly.Load(@”C:\01文件\05Svn\BlogsCode\Blogs\Blogs.Web\bin\Blogs.BLL”);<span style=”color: #008000;”>//<span style=”color: #008000;”>这里会报错
<span style=”color: #008000;”>//<span style=”color: #008000;”>使用Load可以加载当前程序bin目录行下的程序集或者系统程序集

<span style=”color: #008000;”>//<span style=”color: #008000;”>这里TClass可以是一个接口,那么可以在外面的dll任意实现了。
TClass obj = (TClass)asm2.CreateInstance(<span style=”color: #800000;”>”<span style=”color: #800000;”>net.tclass<span style=”color: #800000;”>”,<span style=”color: #0000ff;”>true);<span style=”color: #008000;”>//<span style=”color: #008000;”>true:不区分大小写
obj.fun();<span style=”color: #008000;”>//<span style=”color: #008000;”>调用动态加载的dll中的方法

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

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

补充:跨程序集反射

如果我们反射A.dll,而A.dll中引用了B.dll,那么在assembly.GetTypes(); //运行到这个地方会弹出如下错误描述

“未处理 System.Reflection.ReflectionTypeLoadException Message=”无法加载一个或多个请求的类型。有关更多信息,请检索LoaderExceptions属性。”

这种情况可以

Assembly assembly =  Assembly.LoadFrom(“A.dll”) ;Type type = assembly.GetType(“xxx.myclassname”) ; //

本文以同步至《》