C# 反射浅析
反射是一个运行库类型发现的过程。通过反射可以得到一个给定程序集所包含的所有类型的列表,这个列表包括给定类型中定义的方法、字段、属性和事件。此外,通过反射也可以动态的发现一组给定类支持的接口、方法的参数和其他相关信息,例如基类、命名空间、数据清单等。
一、System.Reflection命名空间内的各类型
- Assembly
通过它可以加载、了解和操纵一个程序集。
- AssemblyName
通过它可以找到大量隐藏在程序集的身份中德信息,如版本信息、区域信息。
- EventInfo
事件的信息
- FieldInfo
字段的信息
- MethodInfo
方法的信息
- ParameterInfo
参数的信息
- PropertyInfo
属性信息
- MemberInfo
是抽象基类,为EventInfo、FieldInfo、MethodInfo、PropertyInfo等类型定义了公共的行为。
- Module
用来访问带有多文件程序集的给定模块。
二、System.Type类
- Is***
用来检查一个类型的元数据,如IsAbstract、IsClass、IsValueType等等。
- Get***
用来从类型得到指定项目,如GetEvent()得到类型的一个指定的事件(EventInfo)。
另外,这些方法都有一个单数版本和一个复数版本。如GetEvent()对应有一个复数版本GetEvents()。
- FindMembers()
根据查询条件返回一个MemberInfo类型的数组。
- GetType()
该静态方法根据一个字符串名称返回一个Type实例。
- InvokeMember()
对给定项目进行晚期绑定。
三、得到一个Type类型实例的三种方法
- 使用System.Object.GetType()
Person pe = new Person(); Type t = pe.GetType();
- 使用Sytem.Type.GetType()静态方法,参数为类型的完全限定名
Type t = Type.GetType("Entity.Person");
该方法被重载,允许指定两个布尔类型的参数,一个用来控制当前类型不能找到时是否抛出异常,另一个用来指示是否区分字符串大小写。
Type t = Type.GetType("Entity.Person",false,true);
注意到传入的字符串并没有包含类型所在的程序集信息,此时该类型便被认为是定义在当前执行的程序集中的。
要得到一个外部私有程序集的类型元数据时,字符串参数必须使用类型完全限定名加上类型所在程序集的友好名字。
Type t = Type.GetType("Entity.Person","Entity");
"Entity"即为类型所在程序集的友好名字 嵌套类型:传入的字符串可以指定一个+标记来表示一个嵌套类型,如希望得到一个嵌套在person类中的枚举类型City的类型信息,则可以这样
Type t = Type.GetType("Entity.person+City");
- 使用typeof运算符
Type t = typeof(person);
三种方法的比较:
第一种方法必须先建立一个实例,而后两种方法不必先建立实例。但使用typeof运算符仍然需要知道类型的编译时信息,而使用System.Type.GetType()静态方法不需要知道类型的编译时信息,所以是首选方法。
四、最简单的C#反射实例
namespace ReflectionTest { public class WriteTest { public void WriteString(string s, int i) { Console.WriteLine("WriteString:" + s + i.ToString()); } public static void StaticWriteString(string s) { Console.WriteLine("StaticWriteString:" + s); } public static void NoneParaWriteString() { Console.WriteLine("NoParaWriteString"); } } }
namespace TestApp { class Program { static void Main(string[] args) { Assembly ass; Type type; Object obj; Object any = new Object(); ass = Assembly.LoadFile(@"F:\GitHub\ReflectionStudy\TestApp\ReflectionStudy.dll"); type = ass.GetType("ReflectionTest.WriteTest"); MethodInfo method = type.GetMethod("WriteString"); string test = "test"; int i = 1; Object[] paramethors = new Object[] { test, i }; obj = ass.CreateInstance("ReflectionTest.WriteTest"); method.Invoke(obj, paramethors); method = type.GetMethod("StaticWriteString"); method.Invoke(null, new string[] { "test" }); method.Invoke(obj, new string[] { "test" }); method.Invoke(any, new string[] { "test" }); method = type.GetMethod("NoneParaWriteString"); method.Invoke(null, null); } } }
从上面的总结中可以看出,对于外部调用的动态库应用反射时要用到Assembly.LoadFile(),然后才是获取类型、执行方法等;当用反射创建当前程序集中对象实例或执行某个类下静态方法时只需通过Type.GetType("类的完整名")。