程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。

您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。

优缺点

优点:

  1. 反射提高了程序的灵活性和扩展性。  2. 降低耦合性,提高自适应能力。  3. 它允许程序创建和控制任何类的对象,无需提前硬编码目标类。

缺点:

  1. 性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。

  2. 使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。

反射(Reflection)有下列用途:

  1. 它允许在运行时查看特性(attribute)信息。

  2. 它允许审查集合中的各种类型,以及实例化这些类型。

  3. 它允许延迟绑定的方法和属性(property)。

我们已经在上面的章节中提到过,使用反射(Reflection)可以查看特性(attribute)信息。

System.Reflection 类的 MemberInfo 对象需要被初始化,用于发现与类相关的特性(attribute)。为了做到这点,您可以定义目标类的一个对象,如下:

下面的程序演示了这点:

  1. using System;
  2. [AttributeUsage(AttributeTargets.All)]
  3. public class HelpAttribute : System.Attribute
  4. {
  5. public readonly string Url;
  6. public string Topic // Topic 是一个命名(named)参数
  7. {
  8. get
  9. {
  10. return topic;
  11. }
  12. set
  13. {
  14. topic = value;
  15. }
  16. }
  17. public HelpAttribute(string url) // url 是一个定位(positional)参数
  18. {
  19. this.Url = url;
  20. }
  21. private string topic;
  22. }
  23. [HelpAttribute("Information on the class MyClass")]
  24. class MyClass
  25. {
  26. }
  27. namespace AttributeAppl
  28. {
  29. class Program
  30. {
  31. static void Main(string[] args)
  32. {
  33. System.Reflection.MemberInfo info = typeof(MyClass);
  34. object[] attributes = info.GetCustomAttributes(true);
  35. for (int i = 0; i < attributes.Length; i++)
  36. {
  37. System.Console.WriteLine(attributes[i]);
  38. }
  39. Console.ReadKey();
  40. }
  41. }
  42. }

当上面的代码被编译和执行时,它会显示附加到类 MyClass 上的自定义特性:

  1. HelpAttribute

实例

在本实例中,我们将使用在上一章中创建的 DeBugInfo 特性,并使用反射(Reflection)来读取 Rectangle 类中的元数据。

  1. using System;
  2. using System.Reflection;
  3. namespace BugFixApplication
  4. {
  5. // 一个自定义特性 BugFix 被赋给类及其成员
  6. [AttributeUsage(AttributeTargets.Class |
  7. AttributeTargets.Constructor |
  8. AttributeTargets.Field |
  9. AttributeTargets.Method |
  10. AttributeTargets.Property,
  11. AllowMultiple = true)]
  12. public class DeBugInfo : System.Attribute
  13. {
  14. private int bugNo;
  15. private string developer;
  16. private string lastReview;
  17. public string message;
  18. public DeBugInfo(int bg, string dev, string d)
  19. {
  20. this.bugNo = bg;
  21. this.developer = dev;
  22. this.lastReview = d;
  23. }
  24. public int BugNo
  25. {
  26. get
  27. {
  28. return bugNo;
  29. }
  30. }
  31. public string Developer
  32. {
  33. get
  34. {
  35. }
  36. }
  37. public string LastReview
  38. {
  39. get
  40. {
  41. }
  42. }
  43. public string Message
  44. {
  45. get
  46. {
  47. return message;
  48. }
  49. set
  50. {
  51. message = value;
  52. }
  53. }
  54. }
  55. [DeBugInfo(45, "Zara Ali", "12/8/2012",
  56. Message = "Return type mismatch")]
  57. [DeBugInfo(49, "Nuha Ali", "10/10/2012",
  58. Message = "Unused variable")]
  59. class Rectangle
  60. {
  61. // 成员变量
  62. protected double length;
  63. protected double width;
  64. public Rectangle(double l, double w)
  65. {
  66. length = l;
  67. width = w;
  68. }
  69. [DeBugInfo(55, "Zara Ali", "19/10/2012",
  70. Message = "Return type mismatch")]
  71. public double GetArea()
  72. {
  73. return length * width;
  74. }
  75. [DeBugInfo(56, "Zara Ali", "19/10/2012")]
  76. public void Display()
  77. {
  78. Console.WriteLine("Length: {0}", length);
  79. Console.WriteLine("Width: {0}", width);
  80. Console.WriteLine("Area: {0}", GetArea());
  81. }
  82. }//end class Rectangle
  83. class ExecuteRectangle
  84. {
  85. static void Main(string[] args)
  86. {
  87. Rectangle r = new Rectangle(4.5, 7.5);
  88. r.Display();
  89. Type type = typeof(Rectangle);
  90. // 遍历 Rectangle 类的特性
  91. foreach (Object attributes in type.GetCustomAttributes(false))
  92. {
  93. DeBugInfo dbi = (DeBugInfo)attributes;
  94. if (null != dbi)
  95. {
  96. Console.WriteLine("Bug no: {0}", dbi.BugNo);
  97. Console.WriteLine("Developer: {0}", dbi.Developer);
  98. Console.WriteLine("Last Reviewed: {0}",
  99. dbi.LastReview);
  100. Console.WriteLine("Remarks: {0}", dbi.Message);
  101. }
  102. }
  103. // 遍历方法特性
  104. foreach (MethodInfo m in type.GetMethods())
  105. {
  106. foreach (Attribute a in m.GetCustomAttributes(true))
  107. {
  108. DeBugInfo dbi = (DeBugInfo)a;
  109. if (null != dbi)
  110. {
  111. Console.WriteLine("Bug no: {0}, for Method: {1}",
  112. dbi.BugNo, m.Name);
  113. Console.WriteLine("Developer: {0}", dbi.Developer);
  114. Console.WriteLine("Last Reviewed: {0}",
  115. dbi.LastReview);
  116. Console.WriteLine("Remarks: {0}", dbi.Message);
  117. }
  118. }
  119. }
  120. Console.ReadLine();
  121. }
  122. }
  123. }

当上面的代码被编译和执行时,它会产生下列结果:

反射用到的命名空间:

  1. System.Reflection
  2. System.Type
  3. System.Reflection.Assembly

反射用到的主要类:

  System.Type 类--通过这个类可以访问任何给定数据类型的信息。  System.Reflection.Assembly类--它可以用于访问给定程序集的信息,或者把这个程序集加载到程序中。

● System.Type类:

  System.Type 类对于反射起着核心的作用。但它是一个抽象的基类,Type有与每种数据类型对应的派生类,我们使用这个派生类的对象的方法、字段、属性来查找有关该类型的所有信息。  获取给定类型的Type引用有3种常用方式:

  1. 使用 C# typeof 运算符。
  2. Type t = typeof(string);
  3. 使用对象GetType()方法。
  4. string s = "grayworm";
  5. Type t = s.GetType();
  6. 还可以调用Type类的静态方法GetType()。
  7. Type t = Type.GetType("System.String");

上面这三类代码都是获取string类型的Type,在取出string类型的Type引用t后,我们就可以通过t来探测string类型的结构了。

  1. string n = "grayworm";
  2. Type t = n.GetType();
  3. {
  4. Console.WriteLine("{0}\t{1}", mi.MemberType, mi.Name);
  5. }

Type类的属性:

Type类的方法:

  1. GetEvent(), GetEvents():返回EventInfo类型,用于取得该类的事件的信息
  2. GetField(), GetFields():返回FieldInfo类型,用于取得该类的字段(成员变量)的信息
  3. GetInterface(), GetInterfaces():返回InterfaceInfo类型,用于取得该类实现的接口的信息
  4. GetMember(), GetMembers():返回MemberInfo类型,用于取得该类的所有成员的信息
  5. GetMethod(), GetMethods():返回MethodInfo类型,用于取得该类的方法的信息
  6. GetProperty(), GetProperties():返回PropertyInfo类型,用于取得该类的属性的信息
  7. 可以调用这些成员,其方式是调用TypeInvokeMember()方法,或者调用MethodInfo, PropertyInfo和其他类的Invoke()方法。
  1. 查看类中的构造方法:
  2. NewClassw nc = new NewClassw();
  3. Type t = nc.GetType();
  4. ConstructorInfo[] ci = t.GetConstructors(); //获取类的所有构造函数
  5. foreach (ConstructorInfo c in ci) //遍历每一个构造函数
  6. {
  7. ParameterInfo[] ps = c.GetParameters(); //取出每个构造函数的所有参数
  8. foreach (ParameterInfo pi in ps) //遍历并打印所该构造函数的所有参数
  9. {
  10. Console.Write(pi.ParameterType.ToString()+" "+pi.Name+",");
  11. }
  12. Console.WriteLine();
  13. }
  14. 用构造函数动态生成对象:
  15. Type t = typeof(NewClassw);
  16. Type[] pt = new Type[2];
  17. pt[0] = typeof(string);
  18. pt[1] = typeof(string);
  19. //根据参数类型获取构造函数
  20. ConstructorInfo ci = t.GetConstructor(pt);
  21. //构造Object数组,作为构造函数的输入参数
  22. object[] obj = new object[2]{"grayworm","hi.baidu.com/grayworm"};
  23. //调用构造函数生成对象
  24. object o = ci.Invoke(obj);
  25. //调用生成的对象的方法测试是否对象生成成功
  26. //((NewClassw)o).show();
  27. Activator生成对象:
  28. Type t = typeof(NewClassw);
  29. //构造函数的参数
  30. object[] obj = new object[2] { "grayworm", "hi.baidu.com/grayworm" };
  31. //用Activator的CreateInstance静态方法,生成新对象
  32. object o = Activator.CreateInstance(t,"grayworm","hi.baidu.com/grayworm");
  33. //((NewClassw)o).show();
  34. 查看类中的属性:
  35. NewClassw nc = new NewClassw();
  36. Type t = nc.GetType();
  37. PropertyInfo[] pis = t.GetProperties();
  38. foreach(PropertyInfo pi in pis)
  39. {
  40. Console.WriteLine(pi.Name);
  41. }
  42. 查看类中的public方法:
  43. NewClassw nc = new NewClassw();
  44. Type t = nc.GetType();
  45. MethodInfo[] mis = t.GetMethods();
  46. foreach (MethodInfo mi in mis)
  47. {
  48. Console.WriteLine(mi.ReturnType+" "+mi.Name);
  49. }
  50. 查看类中的public字段
  51. NewClassw nc = new NewClassw();
  52. Type t = nc.GetType();
  53. FieldInfo[] fis = t.GetFields();
  54. foreach (FieldInfo fi in fis)
  55. {
  56. Console.WriteLine(fi.Name);
  57. } (http://hi.baidu.com/grayworm)
  58. 用反射生成对象,并调用属性、方法和字段进行操作
  59. NewClassw nc = new NewClassw();
  60. Type t = nc.GetType();
  61. object obj = Activator.CreateInstance(t);
  62. //取得私有字段ID
  63. FieldInfo fi = t.GetField("ID", BindingFlags.NonPublic | BindingFlags.Instance);
  64. //给ID字段赋值
  65. fi.SetValue(obj, "k001");
  66. //取得MyName属性
  67. PropertyInfo pi1 = t.GetProperty("MyName");
  68. //给MyName属性赋值
  69. pi1.SetValue(obj, "grayworm", null);
  70. PropertyInfo pi2 = t.GetProperty("MyInfo");
  71. pi2.SetValue(obj, "hi.baidu.com/grayworm", null);
  72. //取得show方法
  73. MethodInfo mi = t.GetMethod("show");
  74. //调用show方法
  75. mi.Invoke(obj, null);

● System.Reflection.Assembly类

  Assembly类可以获得程序集的信息,也可以动态的加载程序集,以及在程序集中查找类型信息,并创建该类型的实例。  使用Assembly类可以降低程序集之间的耦合,有利于软件结构的合理化。

  1. 通过程序集名称返回Assembly对象
  2. Assembly ass = Assembly.Load("ClassLibrary831");
  3. 通过DLL文件名称返回Assembly对象
  4. Assembly ass = Assembly.LoadFrom("ClassLibrary831.dll");
  5. 通过类型返回Assembly对象
  6. Assembly editorAssembly = Assembly.GetAssembly(typeof(NewClassw));
  7. 通过Assembly获取程序集中类
  8. Type t = ass.GetType("ClassLibrary831.NewClass"); //参数必须是类的全名