这一篇文章来总结下怎么使用反射的。
加载程序集
要加载程序集,可以调用 Assembly的LoadXXX系列方法。
1,Assembly.Load方法
1 //1,从GAC或应用程序基目录加载程序集 2 var assembly = Assembly.Load("ReflectionDemo.A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");//或ReflectionDemo.A也可以
需要注意的是:
1)查找顺序,首先去GAC查找,如果没找到,则去应用程序的基目录查找,如果都没找到,则会抛出FileNotFoundException的异常。
2)Load参数一般为应用程序集名称的长格式,如:ReflectionDemo.A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null,也可以直接ReflectionDemo.A。
2,Assembly.LoadFrom方法
1 //2,从一个url加载程序集 2 var assembly = Assembly.LoadFrom(@"http://www.a.com/ReflectionDemo.A.dll");
Assembly.LoadFrom的内部其实是调用Assembly.Load方法,唯一的不同在于,其可以从一个网络url中加载程序集。
3,Assembly.LoadFile方法
1 //3,从本地路径加载程序集 2 var path = string.Format(@"{0}\{1}", AppDomain.CurrentDomain.BaseDirectory, @"plugins\ReflectionDemo.A.dll"); 3 var assembly = Assembly.LoadFile(path);
Assembly.LoadFile允许从本地不同路径加载程序集。
查找程序集内所有类型
反射的入口Type类,Type对象提供的属性和方法可以获取对明的一切信息,包括:字段,属性,方法和事件等等。
在已经加载了程序集的基础上,能过以下方式可以获取到Type对象。
1 /// <summary> 2 /// 获取程序集中所有的公共类型 3 /// </summary> 4 /// <param name="assembly"></param> 5 static void GetExportedTypes(Assembly assembly) 6 { 7 var types = assembly.GetExportedTypes(); 8 foreach (var item in types) 9 { 10 Console.WriteLine(item.Name); 11 } 12 }
1 /// <summary> 2 /// 获取程序集中具名的类型 3 /// </summary> 4 /// <param name="assembly"></param> 5 static void GetType(Assembly assembly) 6 { 7 var t = assembly.GetType("ReflectionDemo.A.Class1"); 8 Console.WriteLine(t.Name); 9 }
查找类型成员
在命名空间System.Reflection下有一个抽象类MemberInfo,它封装了与类型成员相关的通用属性,每一个类型成员都有一个对应的从MemberInfo派生而来的类型,并且内置了一些特殊的属性特征,如FieldInfo、MethodBase(ContructorInfo、MethodInfo)、PropertyInfo和EventInfo。可以通过调用类型Type对象的GetMembers方法获取该类型的所有成员或相应成员,如下代码(对上面的GetTypes方法的修改)获取全部成员列表:
1 /// <summary> 2 /// 获取类型成员 3 /// </summary> 4 /// <param name="t"></param> 5 static void GetMembers(Type t) 6 { 7 var members= t.GetMembers(); 8 foreach (var item in members) 9 { 10 Console.WriteLine(item.Name); 11 } 12 }
Type对象有一组GetXXX方法是用来获取对象成员的,如下:
GetConstructor/GetConstructors //获取构造函数 GetField/GetFields //获取字段 GetProperty/GetProperties //获取属性 GetMethod/GetMethods //获取方法 GetEvent/GetEvents //获取事件
看下MemberInfo的结构:
构造类型实例
拿到类型和成员相关信息后,就可以创建类型的实例了,创建类型实例有以下几种方法:
1 Activator.CreateInstance() //重载系列 2 Activator.CreateInstanceFrom() //重载系列 3 AppDomain.CurrentDomain.CreateInstance() //重载系列 4 AppDomain.CurrentDomain.CreateInstanceFrom() //重载系列
下面就来创建一个ReflectionDemo.A.Class1的实例(对象),如下代码:
1 /// <summary> 2 /// 创建类型实例 3 /// </summary> 4 static void CreateInstance(Assembly assembly) 5 { 6 var t = assembly.GetType("ReflectionDemo.A.Class1"); 7 var obj = Activator.CreateInstance(t); 8 }
访问实例成员
创建了类型的实例后,就可以调用实例的成员方法了,如下代码:
1 /// <summary> 2 /// 动态调用方法 3 /// </summary> 4 /// <param name="assembly"></param> 5 static void InvokeMethod(Assembly assembly) 6 { 7 var t = assembly.GetType("ReflectionDemo.A.Class1"); 8 var obj = Activator.CreateInstance(t); 9 10 var name = t.InvokeMember("GetName", BindingFlags.InvokeMethod, null, obj, null); 11 }
上面演示了动态调用实例成员,访问实例其它成员可以通过BindingFlags来改变。
反射对泛型的支持
前面演示的都是普通类型,如果是泛型,该怎么处理呢?
首先定义一个泛型类,如下:
1 namespace ReflectionDemo.A 2 { 3 public class Class2<T> where T : class 4 { 5 public string GetName<T>(T name) 6 { 7 return string.Format("generic name,{0}", name.ToString()); 8 } 9 } 10 }
演示一下如何调用泛型类的GetName方法,如下代码:
1 /// <summary> 2 /// 访问泛型类型成员 3 /// </summary> 4 /// <param name="assembly"></param> 5 static void InvokeGenericMethod(Assembly assembly) 6 { 7 var types = assembly.GetExportedTypes(); 8 foreach (var item in types) 9 { 10 if (item.IsGenericType)//1,先判断是否为泛型 11 { 12 var obj = Activator.CreateInstance(item.MakeGenericType(new Type[] { typeof(string) }));//2,在创建泛型类实例前,必须调用MakeGenericType创建一个真正的泛型 13 var methodInfo = obj.GetType().GetMethod("GetName").MakeGenericMethod(new Type[] { typeof(string) });//3,在调用泛型方法前,必须调用MakeGenericMethod创建一个真正的泛型方法 14 15 var name = methodInfo.Invoke(obj, new object[] { "aaa" }); 16 Console.WriteLine(name); 17 } 18 } 19 }