一 引言
反射机制是C#中比较有特色的一种技术。它使我们编程时可以更加灵活,能够通过类或者方法名称能够很方便的构造类和调用方法,虽然有一些微不足道的性能损失。我们先来看一下反射的定义:
反射的定义:审查元数据并收集关於它的类型信息的能力,元数据(编辑后的基本数据单元)就是一大堆表,编译器会创建一个类定义表,一个字段定义表,一个方法定义表等,System.Reflection命名空间包含的几个类,允许你反射(解析)这些元数据的代码。
简单来理解:就是编译器在编译反射相关的代码时,会将所有的类、方法、属性等分别创建一个表,而每个表的元素都对应着一部分元数据中的代码。当我们利用类名称字符串去创建这个类中,程序会在保存类中表中找到我们提供的这个类名称,然后再找到对应的构造函数,并执行。
二、反射中的类型
MemberInfo-反射类的成员ConstructorInfo-反射类的构造函数
FieldInfo-反射类的字段
MethodInfo-反射类的构造函数的方法
PropertyInfo-反射类的构造函数的属性
EventInfo-反射类的构造函数的事件
三 、反射相关操作
3.1 准备测试环境
新建一个C#的类库TestDll作为我们要引入的动态链接库,同时新建一个Winform项目RefDll作为我们的主程序。
在类库中我们添加以下类作为需要用反射引入的类:
public class Test
{
}
3.2 以反射的方式加载dll生成数据集
我们通过Assembly对象的LoadFrom方法来加载dll,生成数据集对象实例:string plugin = "TestDll.dll";
Assembly assembly = Assembly.LoadFrom(plugin);
生成的assembly的就是我们需要的数据集对象,后续所有反射机制的相关操作我们都会在这个对象的基础之上进行,Assembly中还有Load()、LoadFile()其他两个方法来生成数据集信息,区别在这里就不再阐述,自行google或百度。
3.3 获取数据集中所有的数据类型(即我们在dll中定义的类)
Type[] types = assembly.GetTypes();
Type type= types[0];
因为我们在dll中只定义了一个类,所以这里的types只会有一个元素,我们把它提取出来,是为了后续代码解释起来比较方便,实际编程中往往是需要一个循环进行遍历的。
3.4 构造类对象实例
我们现在Test类中添加两个构造函数,一个有参,一个无参,我们将以两种不同的形式来创建Test对象:public class Test
{
private int m_i = 0;
public Test()
{
}
private Test(int i)
{
m_i = i;
}
}
调用无参构造:
object obj1 = assembly.CreateInstance(type.FullName);
或者:
object obj1 = Activator.CreateInstance(type);
调用有参构造:
object[] objs=new object[1];
objs[0]=1;
object obj2 = assembly.CreateInstance(type.FullName, false, BindingFlags.CreateInstance, null, objs, null, null);
或者:
object obj2 = Activator.CreateInstance(type, objs);
我们利用了Assembly或者Activator的CreateInstance方法生成对象实例,在调用有参构造函数中,CreateInstance的重载版本使用了几个参数,第二个参数是指查找类名时是否忽略大小写,这里false表示不忽略。第三个参数是搜索的标志位,这里我们要创建类实例,所以使用了BindingFlags.CreateInstance,或者使用BindingFlags.Default也可以。第5个参数,当然就是我们需要传入的构造函数的参数列表。如需详细了解CreateInstance的用法,自行MSDN。
3.5 调用类中的方法
我们现在Test类中添加一个用来测试的方法:public void fun1()
{
MessageBox.Show("func1");
}
我们将利用反射来调用这个方法,首先获取类中所有的方法列表:
MethodInfo[] minfos = type.GetMethods();
MethodInfo就是类中方法的信息,我们通过遍历,找到我们所需要方法的MethodInfo,然后利用MethodInfo的Invoke或者Type的InvokeMember触发方法。
foreach (MethodInfo me in minfos)
{
if (me.Name == "func1")
{
me.Invoke(obj1, null);
type.InvokeMember(me.Name,BindingFlags.InvokeMethod,null,obj1,null);
}
}
MethodInfo.Invoke接收两个参数,第一个是我们创建的类对象实例,第二个是调用的方法所传递的参数列表,object[]类型,因为func1的参数为空,所以这里传递了null。Type .InvokeMember有效的参数和MethodInfo.Invoke类似,只不过多了一个过虑标志BindingFlags.InvokeMethod。
3.6 获取类的所有字段
我们先将Test类中的m_i成员属性改成公有:public int m_i = 0;
然后通过下面的代码可以获取m_i字段:
FieldInfo[] finfo = type.GetFields();
3.7 获取类的所有事件
在Test类中添加如下自定义事件://定义委托
public delegate void BtnClickHandle(object sender, EventArgs e);
//定义事件
public event BtnClickHandle UserControlBtnClicked;
获取定义的该事件:
EventInfo[] einfo = type.GetEvents();
3.8 获取构造函数
ConstructorInfo[] cinfo = type.GetConstructors();
3.9 获取所有成员
MemberInfo[] memInfo = type.GetMembers();
Github位置:
https://github.com/HymanLiuTS/CSGroup
克隆本项目:
git clone git@github.com:HymanLiuTS/ CSGroup.git
获取本文源代码:
git checkout CSL01