反射---Reflection学习
在软件开发中,常常需要一些动态的操作。比如根据用户输入的信息动态的创建一个类或者调用类中的方法。
反射概述
程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。可以使用反射动态的创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或者访问其字段和属性。
反射通常有以下用途:
- 使用Assembly定义和加载程序集。加载在程序集清单中列出的模块,以及从此程序集中查找类型并创建该类型的实例。
- 使用Module了解:包含模块的程序集以及模块中的类,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。
- 使用ConstructorInfo了解:构造函数的名称、参数、访问修饰符和实现详细信息(如abstract或virtual)。使用Type的GetConstructors或GetConstructor方法来调用特定的构造函数。
- 使用MethodInfo了解:方法的名称、参数、访问修饰符和实现详细信息(如abstract或virtual)。使用Type的GetMethods或GetMethod方法来调用特定的方法。
- 使用FieldInfo了解:字段的名称、访问修饰符和实现详细信息(如static),并获取或设置字段值。
- 使用EventInfo了解:事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等;并添加或移除事件处理程序。
- 使用PropertyInfo了解:属性的名称、数据类型、声明类型、反射类型和只读或可写状态等;并获取或设置属性值。
- 使用ParameterInfo了解:参数的名称、数据类型、参数是输入参数还是输出参数、,以及参数在方法签名中的位置。
查看类型信息
当反射请求加载的类型时,CLR为它创建一个Type。可以使用Type对象的方法、字段、属性和嵌套类来查找有关该类型的所有信息。
- 在使用Assembly.GetType或Assembly.GetTypes时,传入所需类型的名称,可以从尚未加载的程序集中获取Type对象。
- 使用Type.GetType可从已加载的程序集中获取Type对象。
- 使用Module.GetType和Module.GetTypes可获取模块Type对象。
下面实例说明如何从已加载的程序集中获取Type对象。
1: // Loads an assembly using its file name.
2: Assembly a = Assembly.LoadFrom ("MyExe.exe");
3: // Gets the type names from the assembly.
4: Type [] types2 = a.GetTypes ();
5: foreach (Type t in types2)
6: {
7: Console.WriteLine (t.FullName);
8: }
在获取了一个Type对象之后,可以采用很多方法来获取与该类型有关的成员的有关信息。如:Type.GetMembers方法获取有关该类型的所有成员信息。
下面示例显示如何列出一个类的构造函数。
1: // This program lists all the public constructors
2: // of the System.String class.
3: using System;
4: using System.Reflection;
5: class ListMembers {
6: public static void Main(String[] args) {
7: Type t = typeof(System.String);
8: Console.WriteLine ("Listing all the public constructors of the {0} type", t);
9: // Constructors.
10: ConstructorInfo[] ci = t.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
11: Console.WriteLine ("//Constructors");
12: PrintMembers (ci);
13: }
14: public static void PrintMembers(MemberInfo [] ms) {
15: foreach (MemberInfo m in ms) {
16: Console.WriteLine ("{0}{1}", " ", m);
17: }
18: Console.WriteLine();
19: }
20: }
动态加载和使用类型
在写这个内容之前,先写一些早期绑定、动态绑定和后期绑定。这里的绑定指的是与类之间建立某种联系的过程。
早期绑定,举个简单例子,我们写一个控制台程序:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using ClassLibraryTest;
6:
7: namespace EarlyBindingDemo
8: {
9: class Program
10: {
11: static void Main(string[] args)
12: {
13: Class1 clazz = new Class1();
14: Console.Write(clazz.getHello());
15: Console.ReadKey();
16: }
17: }
18: }
Class1文件为:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5:
6: namespace ClassLibraryTest
7: {
8: public class Class1
9: {
10: public string getHello()
11: {
12: return "Hello!";
13: }
14: }
15: }
在项目EarlyBindingDemo中添加对Class1.dll的引用,然后在Rrogram里实例化Class1,调用Class的getHello方法。经过编译器编译后,这样一个过程就已经卸载程序集里面了,这就叫做“早期绑定”。
如果EarlyBindingDemo是在完全未知Class1的情况下去调用Class1,这就叫做“后期绑定”。
1: using System;
2: using System.Collections.Generic;
3: using System.Text;
4: using System.Reflection;
5: using System.Windows.Forms;
6: namespace EarlyBindingDemo
7: {
8: class Program
9: {
10: static void Main(string[] args)
11: {
12: Assembly assembly = Assembly.LoadFile(Application.StartupPath + \\ClassLibrary1Test.dll); //加载指定路径上的程序集文件的内容。
13: Type type = assembly.GetType("ClassLibraryTest.Class1");
14: MethodInfo methodInfo = type.GetMethod("Hello");
15: object obj = assembly.CreateInstance("ClassLibraryTest.Class1");//从此程序集中查找指定的类型创建它的实例。
16: Console.WriteLine(methodInfo.Invoke(obj,null));
17: }
18: }
19: }
而“动态绑定”就是指建立了部分联系,一般是在虚方法或者抽象方法调用时,多态机制会在执行期间根据实际对象来确定要执行的代码。
自定义绑定
当通过反射进行后期绑定时,必须使用自定义绑定来控制绑定。Binder类提供了对成员选择和调用的自定义控制。利用自定义绑定,可以在运行时加载程序集,获取有关该程序集中类型的信息,然后对该类型调用方法或访问该类型的字段或属性。如果在编译时不知道对象的类型时,就可以使用这种方法。
InvokeMember 和 CreateInstance
使用 Type.InvokeMember 可调用类型的成员。各个类(如 System.Activator 和 System.Reflection.Assembly)的 CreateInstance 方法是 InvokeMember 的特殊形式,它们可新建特定类型的实例。Binder 类用于在这些方法中进行重载决策和参数强制。