开篇先熟悉两个小概念:
早绑定:是指在编译时绑定对象的类型
晚绑定:是指在运行时才绑定对象的类型。
当然我们提到上面两个概念,肯定是为了引入今天的主题——利用反射实现晚绑定(也就是动态的加载类型,并调用它们)。
我暂时只是为了测试的方便先定义一个不能执行的程序集(Person.dll)无需写的完善,仅仅作为测试使用,之后我们在这个程序中调用它。
person.dll内部如下:
1 using System; 2 public class Chinese 3 { 4 private string language; 5 private string name; 6 private int age; 7 private string like; 8 public string Like 9 { 10 get { return like; } 11 set { like = value; } 12 } 13 public string Language 14 { 15 get { return language; } 16 set { language = value; } 17 } 18 public string Name 19 { 20 get { return name; } 21 set { name = value; } 22 } 23 public int Age 24 { 25 get { return age; } 26 set { age = value; } 27 } 28 public virtual void Favourite() 29 { 30 Console.WriteLine(name+"今年"+age+岁"喜欢的运动是:"+this.like); 31 } 32 public Chinese() 33 { 34 } 35 public Chiese(string name, int age) 36 { 37 this.name = name; 38 this.age = age; 39 } 40 }
注意:当我们不用vs生成注意程序集的时候,一定要注意主程序运行时不能低于该程序集的运行时,默认的使用命令提示执行时使用的是最新的运行时:例如:我这里执行了:
之后执行主程序报错,于是更新了一下运行时版本就OK了。
如果我们这样定义:就会发生早绑定,因为编译时,编译器将从程序集中导入Chinese类。
Chiesep1=new Chiese("小强",18);
那么我们怎么实现晚绑定呢?
现在我们再看下开篇晚绑定的概念,也就是说我们这里实现的效果是:不会再元数据中嵌入对类型的引用,而是在运行是通过反射来实现。现在我们就开始动态加载类型和调用方法。先看一种方式:
- 使用Assembly类的Load方法来动态的来动态的加载指定的程序集。
- 使用Assembly类的GetType方法来动态的加载指定的类型。
- 通过Type类的InvokeMember方法来调用Type对象所表示的类型方法。
下面的代码演示如歌动态的加载程序集Person.dll,动态的加载Chinese类型,并调用Favourite方法:
1 using System; 2 using System.IO; 3 using System.Reflection; 4 5 namespace 动态的加载类型 6 { 7 class Program 8 { 9 static void Main() 10 { 11 //加载程序集 12 string assemblyPath = Path.Combine(Environment.CurrentDirectory, "person.dll"); 13 Assembly a = Assembly.LoadFrom(assemblyPath); 14 //获取指定的类型 15 Type t = a.GetType("Chinese"); 16 //构造类型实例 17 Object[] args = new Object[] { "小强",18 }; 18 Object obj = t.InvokeMember(null, 19 BindingFlags.DeclaredOnly | //指定绑定类型 20 BindingFlags.Public | BindingFlags.NonPublic | 21 BindingFlags.Instance | BindingFlags.CreateInstance, null, null, args); 22 Console.WriteLine("新创建的类型: " + obj.GetType().ToString()); 23 Console.WriteLine("================"); 24 //给字段like赋值 25 t.InvokeMember("like", 26 BindingFlags.DeclaredOnly | 27 BindingFlags.Public | BindingFlags.NonPublic | 28 BindingFlags.Instance | BindingFlags.SetField, null, obj, new Object[] { "抓篮球" }); 29 //调用方法 30 t.InvokeMember("Favourite", BindingFlags.DeclaredOnly | 31 BindingFlags.Public | BindingFlags.NonPublic | 32 BindingFlags.Instance | BindingFlags.InvokeMethod, null, obj, null); 33 Console.ReadKey(); 34 } 35 } 36 }
利用System.Activator类
承接上面的实例更简单的加载类型的方法,就是利用System.Activator类。
该类包含特定的方法,用以在本地或从远程创建对象类型,或获取对现有远程对象的引用。此类不能被继承。
步骤如下:
1.加载程序集并调用GetType方法获取目标类型对象
2.调用Activator.CreateInstance(Type)使用指定类型的默认构造函数来创建该类型的实例,并通过Type对象的GetMethod方法来获取MethodInfo对象
3.使用MethodInfo对象的Invoke方法来动态的执行方法。
1 using System; 2 using System.Reflection; 3 4 namespace Test 5 { 6 class Program 7 { 8 static void Main() 9 { 10 Assembly a = Assembly.Load("person.dll"); 11 Type t = a.GetType("Chinese"); 12 Object[] args = new Object[] { "小强", 18 }; 13 object obj = Activator.CreateInstance(t, args); 14 MethodInfo mi = t.GetMethod("Favourite"); 15 mi.Invoke(obj, null); 16 Console.ReadKey(); 17 } 18 } 19 }
执行效果图: