1、程序集
程序集经由编译器编译得到,供进一步编译执行的中间产物,在Windows系统中,他一般表现为后缀为.dll(库文件)或是.exe(可执行文件)的格式
即:程序集是一个代码集合,现在写的所有代码最终都会被编译器翻译为一个程序集供别人使用
2、元数据
元数据是用来描述数据的数据,不仅用于程序,也在别的领域有所应用
即程序中的类、类中的函数、变量等信息就是程序的元数据。有关程序以及类型的数据被称为元数据,它们保存在程序集中
3、反射的概念
程序在运行时,可以查看其他程序集或者自身的元数据。一个运行的程序查本身或者其他程序的元数据的行为就叫反射。
即在程序运行时通过反射可以得到其他程序集代码的各种信息,例如类、函数、变量、对象等等,进行实例化、执行操作
4、反射的作用
因为反射可以在程序编译后获得信息,所以它提高了程序的拓展性和灵活性
(1)程序运行时得到所有元数据,包括元数据的特性
(2)程序运行时,实例化对象、操作对象
(3)程序运行时创建新对象,用这些对象执行任务
Unity的基本工作机制就是建立在反射的基础上
5、语法相关
(1)Type(类的信息类)
Type是反射功能的基础,是访问元数据的主要方式,使用Type的成员获取有关类型声明的信息
有关类型成员如构造函数、方法、字段、属性和类的事件
i. 获取Type
object中的GetType()方法可以获取对象的Type
int a = 0; Type type = a.GetType(); Console.WriteLine(type);
通过typeof关键字,传入类名,也可以得到对象的Type(常用于同一个程序集中)
Type type2 = typeof(int); Console.WriteLine(type2);
通过类的名字,也可以获取类型(常用于从其他程序集获取)
注意!!类名必须要包含命名空间,不然找不到
Type type3 = Type.GetType("System.Int32") Console.WriteLine(type3);
在以上例子中,type,type2,type3指向堆空间的同一个地址,因为元数据只有一份
ii. 得到类的程序集信息
可以通过Type得到类型所在程序集信息
Console.WriteLine(type.Assembly);
iii.获取类中的所有公共成员
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); } }
首先得到Type
然后得到所有公共成员
需要引用命名空间 using System.Reflection;
Type type = typeof(Test); MemberInfo[] infos = type.GetMembers(); for(int i = 0; i < infos.Length; ++i) { Console.WriteLine(infos[i]); }
IV.获取类的公共构造函数并调用
获取所有构造函数
ConstructorInfo[] ctors = type.GetConstructors(); for(int i = 0; i < ctors.Length; ++i) { Console.WriteLine(ctors[i]); }
获取其中一个构造函数并执行
得构造函数传入 Type数组,数组中内容按顺序是参数类型
执行构造函数传入 object数组,表示按顺序传入的参数
例如得到无参构造
ConstructorInfo info = type.GetConstructor(new Type[0]); // 无参构造 // 执行无参构造,没有参数传 null Test obj = info.Invoke(null) as Test; Console.WriteLine(obj.j);
有参构造
ConstructorInfo info = type.GetConstructor(new Type[] { typeof(int) }); Test obj = info.Invoke(new object[] { 2 }) as Test; obj.Speak(); ConstructorInfo info = type.GetConstructor(new Type[] { typeof(int), typeof(string) }); Test obj = info.Invoke(new object[] { 4, "44444" }) as Test; Console.WriteLine(obj.str);
V.获取类的公共成员变量
得到所有成员变量
FieldInfo[] fieldInfos = type.GetFields(); for(int i = 0; i < fieldInfos.Length; ++i) { Console.WriteLine(fieldInfos[i]); }
得到指定名称的公共成员变量
FieldInfo infoJ = type.GetField("j"); Console.WriteLine(infoJ);
通过反射获取和设置对象的值
通过反射获取对象的某个变量的值
Test test = new Test(); test.j = 99; test.str = "9999"; Console.WriteLine(infoJ.GetValue(test));
通过反射设置指定对象的某个变量的值
infoJ.SetValue(test, 100); Console.WriteLine(infoJ.GetValue(test));
如果要获得字段的类型和字段的名字,可以通过
fieldInfos[i].FieldType.Name fieldInfos[i].Name
VI.获取类的公共成员方法
通过Type类中的GetMethod方法
MethodInfo是方法的反射信息
Type strType = typeof(string); MethodInfo[] methods = strType.GetMethods(); for(int i = 0; i < methods.Length; ++i) { Console.WriteLine(methods[i]); }
如果存在方法重载,用Type数组表示参数类型
MethodInfo subStr = strType.GetMethod("Substring", new Type[] { typeof(int), typeof(int) });
调用该方法
string str = "Hello World!"; object result = subStr.Invoke(str, new Object[] { 7, 5 }); // string result = subStr.Invoke(str, new Object[] { 7, 5 }) as string; Console.WriteLine(result);
注意:如果是静态方法,Invoke中的第一个参数传null即可,即不需要对象调用
VII.其他
得枚举:GetEnumName(s)
得事件:GetEvent(s)
得接口:GetInterface(s)
得属性:GetProperty(ies)
(2)Assembly
程序集类,主要用来加载其他程序集,加载后才能用Type来使用其他程序集中的信息
如果想要使用不是自己程序集中的内容,需要先加载程序集,比如 dll 文件(库文件)
简单的把库文件看成一种代码仓库,它提供给使用者一些可以直接拿来用的变量、函数或类
三种加载程序集的函数
一般用来加载在同一文件夹下的其他程序集
Assembly assembly = Assembly.Load("程序集名称");
一般用来加载不在同一文件夹下的其他程序集
Assembly assembly = Assembly.LoadFrom("包含程序集清单的文件的名称或路径"); Assembly assembly = Assembly.LoadFile("要加载的文件的完全限定路径");
注意:如果VS没有生成dll文件,在程序的右键属性->应用程序->输出类型->选择类库->重新生成
这里以多线程的练习作为dll为例
I.先加载一个指定程序集
Assembly assembly = Assembly.LoadFrom(@"C:\Users\Waylon\Desktop\ConsoleApp1\ConsoleApp2\bin\Debug\ConsoleApp2.dll"); Type[] types = assembly.GetTypes(); for(int i = 0; i < types.Length; ++i) { Console.WriteLine(types[i]); }
II.再加载程序集中的一个类对象,之后才能使用反射
Type iconType = assembly.GetType("ConsoleApp2.Icon"); MemberInfo[] members = iconType.GetMembers(); for(int i = 0; i < members.Length; ++i) { Console.WriteLine(members[i]); }
通过反射实例化一个icon对象,由于实例化需要传入方向的枚举类型,因此首先要通过反射得到这个枚举(枚举比较特殊)
Type moveDir = assembly.GetType("ConsoleApp2.E_MoveDir"); FieldInfo right = moveDir.GetField("Right"); // 只是得到了枚举的一个反射信息,而不是枚举的值 // 直接实例化对象,枚举的取值比较特殊 object iconObj = Activator.CreateInstance(iconType, new object[] { 10, 20, right.GetValue(null) }); // 通过反射得到对象的方法 MethodInfo move = iconType.GetMethod("Move"); MethodInfo draw = iconType.GetMethod("Draw"); MethodInfo clear = iconType.GetMethod("Clear"); Console.Clear(); while (true) { Thread.Sleep(300); clear.Invoke(iconObj, null); move.Invoke(iconObj, null); draw.Invoke(iconObj, null); }
III.类库工程的创建
新建项目选择类库项目,不能执行,全是代码,生成dll文件
(3)Activator
用于快速实例化对象的类,用于将Type对象快捷实例化对象,先得到Type,然后快速实例化一个对象
I.无参构造
Type type = typeof(Test); Test testObj = Activator.CreateInstance(type) as Test; Console.WriteLine(testObj.str);
II.有参构造
Test testObj = Activator.CreateInstance(type, 99) as Test; testObj.Speak(); Test testObj = Activator.CreateInstance(type, 99, "testObj") as Test; Console.WriteLine(testObj.str);