C#反射
一、反射基础理论
1.什么是程序集
- 程序集是经由编译器得到的,供进一步编译的中间产物。
- 在Windows系统中,它一般表现为.dll(库文件)或者是.exe(可执行文件)的格式。
2.元数据
- 元数据就是用来描述数据的数据。
- 程序中的类,类中的函数、变量等等信息就是程序的元数据。
- 有关程序以及类型的数据被称为元数据,它们保存在程序集中。
3.反射的概念
- 程序在运行时,可以查看其它程序集或自身的元数据
- 一个运行的程序查看本身或者其他升序的元数据的行为叫做反射
- 也就是说,在程序运行的时候,我们可以通过反射得到自身或者其他程序集的各种信息。来做我们想做的事情。
4.反射的作用
- 因为反射可以在程序编译后获取信息,提高了程序的拓展性灵活性。
- 程序运行时得到所有的元数据,包括元数据的特性
- 程序运行时,实例化对象、操作对象
- 程序在运行时创建新对象,用这些对象去执行任务
二、相关API
Type
Type(类的信息类)
- 它是访问的基础
- 它是访问元数据的主要方式
- 使用Type的成员获取有关类型声明的成员信息(如构造函数、方法、字段、属性和类的事件等)
获取Type的三种方法
1.直接通过索要获取对象中的GetType()
方法可以获取对象的Type。
int a = 42;
Type type = a.GetType();
Console.WriteLine(type);
打印的结果:
2.通过typeof关键字获取,传入类名,也可以得到对象的type
Type type2 = typeof(int);
Console.WriteLine(type2);
打印的结果为:
3.通过类名获取
类名必须要包括命名空间,否则访问不到
Type type3 = Type.GetType("System.Int32");
Console.WriteLine(type3);
说明:【上述三种获取都是得到了System.Int32,它们指向的地址是一样的!】
获取类的程序集信息
int a = 42;
Type type = a.GetType();
Console.WriteLine(type.Assemble);
打印结果:
测试代码
测试代码
声明了一个测试类,用反射获取这个类相关的公共成员变量并执行它们。
class Test {
private int i = 1;
public int j = 0;
public string str = "123";
public Test() { }
public Test(int i) {
this.i = i;
}
public Test(int i,string str):this(i)
{
this.str = str;
}
public void Speak()
{
Console.WriteLine(i);
}
}
获取类中的公共成员
相关API: MemberInfo Type.GetMembers()
Type t = typeof(Test);
//得到所有的公共成员变量
MemberInfo[] infos = t.GetMembers();
for (int i = 0; i < infos.Length; i++)
{
Console.WriteLine(infos[i]);
}
获取所有的公共的构造函数并调用
相关API: ConstructorInfo
Type.GetConstructors()
ConstructorInfo[] ctor = t.GetConstructors();
for (int i = 0; i < ctor.Length; i++)
{
Console.WriteLine(ctor[i]);
}
获取其中一个构造函数并执行
- 在得到构造函数的时候,传入Type数组,数组中的内容顺序是参数类型
- 在执行构造函数的时候,传入object数组,按照顺序传入参数
1.获取无参的构造函数
- 因为这里没有参数,我们传入的数组声明为0就可以了
ConstructorInfo info = t.GetConstructor(new Type[0]);
执行无参构造
因为没有参数,传入null
即可。
info.Invoke(null);
Test test = info.Invoke(null) as Test;
2.得到有参构造并且执行传递参数
//这里得到的是Test类中的一个参数的构造函数
ConstructorInfo info2 = t.GetConstructor(new Type[] { typeof(int) });
//传入参数的时候需要严格按照顺序传入
info2.Invoke(new object[] { 3 });
//这里得到的是Test两个参数的构造函数
ConstructorInfo info3 = t.GetConstructor(new Type[] { typeof(int), typeof(string) });
info3.Invoke(new object[] { 2, "356" });
获取类中的公共成员变量
相关API: FieldInfo
Type.GetFields()
FieldInfo[] fieldInfos = t.GetFields();
for (int i = 0; i < fieldInfos.Length; i++)
{
Console.WriteLine(fieldInfos[i]);
}
1.获取指定名称的公共成员变量
//这里获取的是Test类中的j
FieldInfo infoj = t.GetField("j");
2.通过反射获取和设置对象的值
Test test2 = new Test();
test2.j = 99;
test2.str = "55555";
2.1通过反射获取对象的某个值
infoj.GetValue(test2) 将对象传递进去
Console.WriteLine("通过反射获取j的值为:"+infoj.GetValue(test2));
2.2通过反射设置指定某个变量的值
infoj.SetValue(test2, 6666)
Console.WriteLine("通过反射设置的值为:"+infoj.GetValue(test2));
结果为:
获取类的公共方法
相关API:MethodInfo
Type.GetMethods()
这里得到string类中的方法为例
Type strType = typeof(string);
MethodInfo[] methods = strType.GetMethods();
for (int i = 0; i < methods.Length; i++)
{
Console.WriteLine(methods[i]);
}
1.如果存在方法的重载,使用type[]数组依次表示参数的类型 (这里获取的是指定方法)
1.method.Invoke(str,new object[] {1,2 });
第一个str
参数相当于哪个对象要执行这个方法,后边是object[]数组用于传递实参。
!!!这里需要说明的通过反射调用(执行
)非静态的方法时,需要传递一个对象进去,而静态方法不需要传递对象进去,直接给一个null即可,因类静态方法是可以通过类名直接点出来的,而我们如果需要使用普通类中的公共成员,需要先实例化,然后才能使用它。!!!
MethodInfo method = strType.GetMethod("Substring", new Type[] { typeof(int), typeof(int) });
string str = "QWEQWERT";
object obj= method.Invoke(str,new object[] {1,2 });
Console.WriteLine(obj);
结果为:
其它相关的
枚举相关API:GetEnumsName GetEnumsNames
事件相关API:GetEvent GetEvents
接口相关API:GetInterface GetInterfaces
属性相关API:GetProperty GetPropertys
三、Assembly程序集类
程序集类的作用:
可以使用其它程序集的代码
- 主要用于加载其它其它程序类,只有加载后才能用Type使用获取其它程序集中的信息。
- 如果想要使用不是自己的程序集中的内容,需要先加载程序集(比如dll 库文件)
加载程序集的方式:
1.加载同一文件夹下的其它程序集
Assembly assembly = Assembly.Load(程序集所在路径);
2.加载不同文件夹下的其它程序集
Assembly assembly = Assembly.LoadFrom(路径);
Assembly assembly = Assembly.LoadFile(路径);
区别:
- Assembly.LoadFile()只载入相应的dll文件,路径写入哪个就加载哪一个。
- Assembly.LoadFrom()它会加载dll文件以及引用的其它dll文件
- 用Assembly.LoadFrom()载入一个Assembly时,会先检查自己是否已经载入过相同名字Assembly,如果有两个不同版本的相同名字的dll,载入第二个版本的dll会会返回第一个dll的文件,使用Assembly.LoadFile(),就能正确载入。
加载其它程序集的一般步骤:
- 先通过
Assembly.LoadFile()
或Assembly.LoadFrom()
加载程序集 - 在通过Type获取想要获取的类对象,之后才能使用反射
- 在通过反射调用,实力化对象
四、Activator(快速实例化对象)
作用:
- 用于快速实例化对象的类
- 用于将Type对象快捷实例化对象
先得到Type,然后快速实例化一个对象
Type testType = typeof(Test);
Activator.CreateInstance(testType); //默认调用无参
//通过里氏替换原则转换成Test
Test t1= Activator.CreateInstance(testType) as Test;
//调用的是Test中一个参数的构造
Test t2=Activator.CreateInstance(testType, 99) as Test;
//调用的是Test中两个参数的构造
Test t3= Activator.CreateInstance(testType, 100,"sdsd") as Test;