反射概述
反射定义
在正式开始讲述反射之前,还是让我们先来看一下微软自己是怎么定义反射的,根据MSDN的解释:反射提供了封装程序集、模块和类型的对象(Type 类型)。可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。如果代码中使用了属性,可以利用反射对它们进行访问。说到底,反射就是一种在程序的执行期通过对程序集的类型元数据的使用动态获取其模块,类型,方法等信息的机制。
为何需要反射
任何一种技术的产生都有其特定的背景和使用需求,同样,反射也不例外,具体来说,我们将在下面的场景中使用到反射技术。
· 查看元数据,这给我们窥探程序集的内部机理提供了一种方便,很多工具也是基于此而开发的,例如ILDASM和Reflector;
· 类型查看,它允许我们检查程序集中的类型并与之交互,这给我们在创建定制脚本方面提供了极大的帮助;
· 后期绑定,该技术通常用于解释型语言,例如脚本语言,因为其程序集中的某些类在编译期间是未知的;
· 运行时动态创建类型。
反射应用
查看元数据
在这里我想举一个查看自定义属性的例子(关于属性的更多内容,请参考我的另一篇文章),为此,我们得先准备一个自定义属性

2 using System.Reflection;
3
4 namespace ViewCustomProperty
5 {
6 class Program
7 {
8 static void Main(string[] args)
9 {
10 MemberInfo memberInfo = typeof(ClassWithCustomProperty);
11 object[] customAttributes = memberInfo.GetCustomAttributes(typeof(BugFixAttribute), false);
12 foreach (object obj in customAttributes)
13 {
14 BugFixAttribute bugFixAttribute = obj as BugFixAttribute;
15 if (bugFixAttribute != null)
16 {
17 Console.WriteLine("BugID: {0}", bugFixAttribute.BugID);
18 Console.WriteLine("Programmer: {0}", bugFixAttribute.Programmer);
19 Console.WriteLine("Date: {0}", bugFixAttribute.Date);
20 Console.WriteLine("Comment: {0}\n", bugFixAttribute.Comment);
21 }
22 }
23 Console.ReadKey();
24 }
25 }
26
27 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
28 public class BugFixAttribute : System.Attribute
29 {
30 public BugFixAttribute(int bugID, string programmer, string date)
31 {
32 this.bugID = bugID;
33 this.programmer = programmer;
34 this.date = date;
35 }
36
37 public int BugID
38 {
39 get
40 {
41 return bugID;
42 }
43 }
44
45 public string Comment
46 {
47 get
48 {
49 return comment;
50 }
51 set
52 {
53 comment = value;
54 }
55 }
56
57 public string Date
58 {
59 get
60 {
61 return date;
62 }
63 }
64
65 public string Programmer
66 {
67 get
68 {
69 return programmer;
70 }
71 }
72
73 private int bugID;
74 private string comment;
75 private string date;
76 private string programmer;
77 }
78
79 [BugFixAttribute(66180, "Chunting Pan", "01/01/2010")]
80 [BugFixAttribute(66181, "Chunting Pan", "12/31/2010", Comment = "Rollback the changes from previous version")]
81 public class ClassWithCustomProperty
82 {
83 public void Display()
84
85 {
86 Console.WriteLine("This is a class with custom property");
87 }
88 }
89 }
运行的结果如下:
BugID: 66180
Programmer: Chunting Pan
Date: 01/01/2010
Comment:
BugID: 66181
Programmer: Chunting Pan
Date: 12/31/2010
Comment: Rollback the changes from previous version
类型查看
类型查看非常简单,在此不想浪费太多时间,让我们直接看一个例子,此例子返回正在运行的程序集的所有类型信息。

2 using System.Reflection;
3
4 namespace TypeDiscovery
5 {
6 class Program
7 {
8 static void Main(string[] args)
9 {
10 Assembly assembly = Assembly.GetExecutingAssembly();
11 Type[] types = assembly.GetTypes();
12 foreach (Type type in types)
13 {
14 Console.WriteLine(type);
15 }
16 Console.ReadKey();
17 }
18 }
19 }
运行结果如下:
TypeDiscovery.Program
后期绑定
程序集A的代码可以实例化并使用一个定义在程序集B中的类型,而该类型可能在A编译的时候并没有被引用,我们把这种类型的联系描述为后期绑定。如果我们深入研究后期绑定,你将会发现这是一种很酷的技术,但限于篇幅的原因,在此不再赘述,仅以下面的例子作为参考,有兴趣的同学可以去研究一下动态语言如Python,Ruby以及插件技术(Plugin)

2 using System.Reflection;
3
4 namespace LateBinding
5 {
6 class Program
7 {
8 static void Main(string[] args)
9 {
10 Assembly assembly = Assembly.GetExecutingAssembly();
11 Type type = assembly.GetType("LateBinding.Employee");
12 Object obj = Activator.CreateInstance(type);
13
14 Type[] paramTypes = new Type[1];
15 paramTypes[0] = Type.GetType("System.String");
16 MethodInfo SayHelloMethodInfo = type.GetMethod("SayHello", paramTypes);
17
18 Object[] parameters = new Object[1];
19 parameters[0] = "panchunting";
20 Object returnVal = SayHelloMethodInfo.Invoke(obj, parameters);
21 Console.WriteLine(returnVal);
22 Console.ReadKey();
23 }
24 }
25
26 public class Employee
27 {
28 public void SayHello(string employeeName)
29 {
30 Console.WriteLine("Hello, {0}", employeeName);
31 }
32 }
33 }
运行结果如下:
Hello, panchunting
运行时动态创建类型
在创建类型之前,我们得有相应的程序集,你可选择创建一个新程序集,也可以选择使用当前的程序集,下面我们分别加以说明。
方式一:引用外部程序集
一般而言,程序集由DLL或EXE文件组成,当然也可以包含资源文件,如JPG等,我们假定这里的程序集只包含单个DLL文件,为此我们先创建一个类库项目

2
3 namespace ReflectionClassLibrary
4 {
5 public class Employee
6 {
7 public Employee(string employeeName)
8 {
9 this.employeeName = employeeName;
10 }
11
12 private string employeeName;
13
14 public void SayHello()
15 {
16 Console.WriteLine("Hello, " + this.employeeName);
17 }
18 }
19 }
下面创建一个控制台应用程序用于反射此程序集(当然你得先添加对此程序集的引用)

2 using System.Reflection;
3 using ReflectionClassLibrary;
4
5 namespace ReflectionEmployee
6 {
7 class Program
8 {
9 static void Main(string[] args)
10 {
11 Assembly assembly = Assembly.LoadFrom("ReflectionClassLibrary.dll");
12 Type type = assembly.GetType("ReflectionClassLibrary.Employee");
13 Employee employee = Activator.CreateInstance(type, "panchunting") as Employee;
14 if (employee != null)
15 {
16 employee.SayHello();
17 }
18 Console.ReadKey();
19 }
20 }
21 }
22
运行结果如下:
Hello, panchunting
方式二,反射的类与反射方法在同一个程序集中

2 using System.Reflection;
3
4 namespace ReflectionAllInOneAssembly
5 {
6 class Program
7 {
8 static void Main(string[] args)
9 {
10 Assembly assembly = Assembly.GetExecutingAssembly();
11 Type type = assembly.GetType("ReflectionAllInOneAssembly.Employee");
12 Employee employee = Activator.CreateInstance(type, "panchunting") as Employee;
13 if (employee != null)
14 {
15 employee.SayHello();
16 }
17 Console.ReadKey();
18 }
19 }
20
21 public class Employee
22 {
23 public Employee(string employeeName)
24 {
25 this.employeeName = employeeName;
26 }
27
28 private string employeeName;
29
30 public void SayHello()
31 {
32 Console.WriteLine("Hello, " + this.employeeName);
33 }
34 }
35 }
反射与抽象工厂
先介绍点业务逻辑知识:Performance有两种类型:Plan和Review,而它们又都有三种类型的Program,即:Competency,JobResponsibility和TrainingDevelopment
创建抽象工厂代码如下:

2 using System.Text;
3 using System.Reflection;
4
5 namespace ReflectionAbstractFactory
6 {
7 public abstract class AbstractFactory
8 {
9 public abstract IProgramType CreateProgram(string ProgramTypeName);
10 }
11
12 public class ReviewProgram : AbstractFactory
13 {
14 public override IProgramType CreateProgram(string ProgramTypeName)
15 {
16 IProgramType ProgramType = null;
17 Type type = Type.GetType("ReflectionAbstractFactory." + ProgramTypeName);
18 if (type != null)
19 {
20 ProgramType = Activator.CreateInstance(type) as IProgramType;
21 }
22 return ProgramType;
23 }
24 }
25
26 public class PlanProgram : AbstractFactory
27 {
28 public override IProgramType CreateProgram(string ProgramTypeName)
29 {
30 IProgramType ProgramType = null;
31 Type type = Type.GetType("ReflectionAbstractFactory." + ProgramTypeName);
32 if (type != null)
33 {
34 ProgramType = Activator.CreateInstance(type) as IProgramType;
35 }
36 return ProgramType;
37 }
38 }
39
40 public interface IProgramType
41 {
42 void Display();
43 }
44
45 public class ReviewCompetency : IProgramType
46 {
47 public void Display()
48 {
49 Console.WriteLine("This is competency program of review");
50 }
51 }
52
53 public class ReviewJobResponsibility : IProgramType
54 {
55 public void Display()
56 {
57 Console.WriteLine("This is job responsibility program of review");
58 }
59 }
60
61 public class ReviewTrainingDevelopment : IProgramType
62 {
63 public void Display()
64 {
65 Console.WriteLine("This is training development program of review");
66 }
67 }
68
69 public class PlanCompetency : IProgramType
70 {
71 public void Display()
72 {
73 Console.WriteLine("This is competency program of plan");
74 }
75 }
76
77 public class PlanJobResponsibility : IProgramType
78 {
79 public void Display()
80 {
81 Console.WriteLine("This is job responsibility program of plan");
82 }
83 }
84
85 public class PlanTrainingDevelopment : IProgramType
86 {
87 public void Display()
88 {
89 Console.WriteLine("This is training development program of plan");
90 }
91 }
92 }
控制台运行结果如下:
Please input the class name, like ReviewCompetency:ReviewCompetency
------------------------------------------------------
This is competency program of review
------------------------------------------------------
Plese input any key to exit
性能
(MSDN)反射的性能损失主要来源于比较类型、遍历成员、调用成员三种情形,其中比较类型耗时最小,调用成员耗时最多,所以尽量减少采用成员动态调用等反射方式可以提高应用程序性能。除此之外,采取后期绑定、避免将反射方法放到循环内产生放大效应等办法均可提升反射性能。
参考及引用
http://msdn.microsoft.com/zh-cn/library/ms173183(VS.80).aspx
http://msdn.microsoft.com/en-us/magazine/cc164170.aspx
http://oreilly.com/catalog/progcsharp/chapter/ch18.html
http://www.codeproject.com/KB/architecture/CSharpClassFactory.aspx
Practical .NET2 and C#2 (Harness the Platform, the Language, the Framework)