"反射"是.NET中的重要技术,通过反射,可以在运行时获得某个类型的各种信息,进而动态的创建对象实例,完成方法调用,属性设置和事件激发。在.NET中“代码特性(Attribute)”就利用了反射技术。
反射技术的基础
反射的特点:允许一个对象的类型带程序运行时才确定。
反射作用:这个技术在实际开发中很有用,它使我们可以动态的替换掉系统中的某一部分而不用重新编译源码。
反射概述
反射的基本作用:运行期,根据对象名就能获得该对象的:类型、方法、属性,还可以获得每个成员的名称,访问权限和参数信息,这些信息都存储于元数据中,因此反射处理的对象是程序集元数据。
注:著名的Reflector软件就是利用反射技术编写的,可以查看.NET程序集中的所有数据类型,并反编译为C#等语言的源代码。
.NET程序集的层次结构
C#程序中声明的“类型”包含成员,比如类中就包含方法,属性,字段等成员,并且按照命名空间进行组织,类就是类型(Type)的示例。在编译程序的时候被打包为程序集(通常拓展名为exe或dll,前者可直接执行,后者必须加载到进程中才能被执行)。
在System.Reflection命名空间内包含多个反射常用的类,下面表格列出了常用的几个类。
类型 作用
Assembly 通过此类可以加载操纵一个程序集,并获取程序集内部信息
EventInfo 该类保存给定的事件信息
FieldInfo 该类保存给定的字段信息
MethodInfo 该类保存给定的方法信息
MemberInfo 该类是一个基类,它定义了EventInfo、FieldInfo、MethodInfo、PropertyInfo的多个公用行为
Module 该类可以使你能访问多个程序集中的给定模块
ParameterInfo 该类保存给定的参数信息
PropertyInfo 该类保存给定的属性信息
上述的信息被存放在模块的源数据中,使用System.Type类代表抽象的数据类型,我们可以使用顶层的Object类提供的GetType()方法来获得它所对应的Type对象。
数据类型的奥秘System.Type类
Type类是一个特殊的数据类型,他是可以表达数据类型信息的数据类型。参照下面的代码理解
public class cla1 { public int i; public void met(){} public string pro{get;set;} }
上诉代码定义了一个public类型的cla1,拥有以下类型
1)类名cla1,公有,
2)父类为Object类
3)公有字段 i
4)公有方法 met()
5)公有属性 pro
上述的信息都被封装到了Type类型的对象中。Type是最常用到的类,通过Type可以得到一个类的内部信息,也可以通过它反射创建一个对象。一般有三个常用的方法可以得到Type对象。
1.利用typeof()得到Type对象
cla1 c1= new cla1();
Type type = typeof(cla1);
2.利用System.Object.GetType()得到Type对象
cla1 c1= new cla1();
Type type = c1.GetType();
3.利用System.Type.GetType()得到Type对象
Type type = Type.GetType("MyAssembly.cla1",false,true) //注意0是类名,参数1表示若找不到对应类时是否抛出异常,参数2表示类名是否区分大小写
三者的区别在于:
typeof()和Type.GetType()是从一个类中获取对象,而Object.GetType()是从一个类的实例获得对象。
而前两者的区别在于:
- Typeof()是运算符而GetType是方法
- GetType()是基类System.Object的方法,因此只有建立一个实例之后才能够被调用(初始化以后)
- Typeof()的参数只能是int,string,String,自定义类型,且不能是实例
- GetType() 和typeof()都返回System.Type的引用。
动态对象的创建与方法的调用
读者读到这里 的时候一定会问,我知道这些信息能做什么,其实 一旦你拿到这些Type对象的信息就可以在需要的时候创建这一类型的对象,并且调用它的方法,并且保存此类型的Assembly文件,并不需要添加程序集引用。
动态对象创建
Assembly类有一个CreateInstance方法,用于创建对象,其定义为
public Object CreateInstance(string typename);
只要传给这个方法一个类型名称的字符串就可以创建一个对象。
class Program { static void Main(string[] args) { Assembly assm = Assembly.Load("fanshe"); Console.WriteLine(assm.FullName); //输出 fanshe, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null //注释上面两行,移除程序集的引用 Assembly assm1 = Assembly.LoadFrom(@"D:\fanshe.dll"); Console.WriteLine(assm1.FullName); ////输出 fanshe, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null //与Assembly.LoadFrom基本一样,只是如果被加载的dll,还依赖其他的dll的话,它不会加载 Assembly assm2 = Assembly.LoadFile(@"D:\fanshe.dll"); Console.WriteLine(assm2.FullName); Console.ReadKey(); } }