C# 反射

反射指程序可以访问、检测和修改它本身状态或行为的一种能力。

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

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

优缺点

优点:

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

缺点:

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

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

一、反射(Reflection)的用途

反射(Reflection)有下列用途:

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

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

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

  4. 它允许在运行时创建新类型,然后使用这些类型执行一些任务。

  (1)使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。

  (2)使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。

  (3)使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如publicprivate)和实现详细信息(如abstractvirtual)等。

  (4)使用MethodInfo了解方法的名称、返回类型、参数、访问修饰符(如publicprivate)和实现详细信息(如abstractvirtual)等。

  (5)使用FieldInfo了解字段的名称、访问修饰符(如publicprivate)和实现详细信息(如static)等,并获取或设置字段值。

  (6)使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。

  (7)使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。

  (8)使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。

二、查看元数据

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

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

  1. System.Reflection.MemberInfo info = typeof(MyClass);

下面的程序演示了这点:

  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. return developer;
  36. }
  37. }
  38. public string LastReview
  39. {
  40. get
  41. {
  42. return lastReview;
  43. }
  44. }
  45. public string Message
  46. {
  47. get
  48. {
  49. return message;
  50. }
  51. set
  52. {
  53. message = value;
  54. }
  55. }
  56. }
  57. [DeBugInfo(45, "Zara Ali", "12/8/2012",
  58. Message = "Return type mismatch")]
  59. [DeBugInfo(49, "Nuha Ali", "10/10/2012",
  60. Message = "Unused variable")]
  61. class Rectangle
  62. {
  63. // 成员变量
  64. protected double length;
  65. protected double width;
  66. public Rectangle(double l, double w)
  67. {
  68. length = l;
  69. width = w;
  70. }
  71. [DeBugInfo(55, "Zara Ali", "19/10/2012",
  72. Message = "Return type mismatch")]
  73. public double GetArea()
  74. {
  75. return length * width;
  76. }
  77. [DeBugInfo(56, "Zara Ali", "19/10/2012")]
  78. public void Display()
  79. {
  80. Console.WriteLine("Length: {0}", length);
  81. Console.WriteLine("Width: {0}", width);
  82. Console.WriteLine("Area: {0}", GetArea());
  83. }
  84. }//end class Rectangle
  85. class ExecuteRectangle
  86. {
  87. static void Main(string[] args)
  88. {
  89. Rectangle r = new Rectangle(4.5, 7.5);
  90. r.Display();
  91. Type type = typeof(Rectangle);
  92. // 遍历 Rectangle 类的特性
  93. foreach (Object attributes in type.GetCustomAttributes(false))
  94. {
  95. DeBugInfo dbi = (DeBugInfo)attributes;
  96. if (null != dbi)
  97. {
  98. Console.WriteLine("Bug no: {0}", dbi.BugNo);
  99. Console.WriteLine("Developer: {0}", dbi.Developer);
  100. Console.WriteLine("Last Reviewed: {0}",
  101. dbi.LastReview);
  102. Console.WriteLine("Remarks: {0}", dbi.Message);
  103. }
  104. }
  105. // 遍历方法特性
  106. foreach (MethodInfo m in type.GetMethods())
  107. {
  108. foreach (Attribute a in m.GetCustomAttributes(true))
  109. {
  110. DeBugInfo dbi = (DeBugInfo)a;
  111. if (null != dbi)
  112. {
  113. Console.WriteLine("Bug no: {0}, for Method: {1}",
  114. dbi.BugNo, m.Name);
  115. Console.WriteLine("Developer: {0}", dbi.Developer);
  116. Console.WriteLine("Last Reviewed: {0}",
  117. dbi.LastReview);
  118. Console.WriteLine("Remarks: {0}", dbi.Message);
  119. }
  120. }
  121. }
  122. Console.ReadLine();
  123. }
  124. }
  125. }

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

  1. Length: 4.5
  2. Width: 7.5
  3. Area: 33.75
  4. Bug No: 49
  5. Developer: Nuha Ali
  6. Last Reviewed: 10/10/2012
  7. Remarks: Unused variable
  8. Bug No: 45
  9. Developer: Zara Ali
  10. Last Reviewed: 12/8/2012
  11. Remarks: Return type mismatch
  12. Bug No: 55, for Method: GetArea
  13. Developer: Zara Ali
  14. Last Reviewed: 19/10/2012
  15. Remarks: Return type mismatch
  16. Bug No: 56, for Method: Display
  17. Developer: Zara Ali
  18. Last Reviewed: 19/10/2012
  19. Remarks:

三、反射的用法

反射用到的命名空间:

  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. foreach (MemberInfo mi in t.GetMembers())
  4. {
  5. Console.WriteLine("{0}\t{1}", mi.MemberType, mi.Name);
  6. }

Type类的属性:

  1. Name 数据类型名
  2. FullName 数据类型的完全限定名(包括命名空间名)
  3. Namespace 定义数据类型的命名空间名
  4. IsAbstract 指示该类型是否是抽象类型
  5. IsArray 指示该类型是否是数组
  6. IsClass 指示该类型是否是类
  7. IsEnum 指示该类型是否是枚举
  8. IsInterface 指示该类型是否是接口
  9. IsPublic 指示该类型是否是公有的
  10. IsSealed 指示该类型是否是密封类
  11. IsValueType 指示该类型是否是值类型

Type类的方法:

  1. GetConstructor(), GetConstructors():返回ConstructorInfo类型,用于取得该类的构造函数的信息
  2. GetEvent(), GetEvents():返回EventInfo类型,用于取得该类的事件的信息
  3. GetField(), GetFields():返回FieldInfo类型,用于取得该类的字段(成员变量)的信息
  4. GetInterface(), GetInterfaces():返回InterfaceInfo类型,用于取得该类实现的接口的信息
  5. GetMember(), GetMembers():返回MemberInfo类型,用于取得该类的所有成员的信息
  6. GetMethod(), GetMethods():返回MethodInfo类型,用于取得该类的方法的信息
  7. GetProperty(), GetProperties():返回PropertyInfo类型,用于取得该类的属性的信息
  8. 可以调用这些成员,其方式是调用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"); //参数必须是类的全名
  9. 通过Assembly获取程序集中所有的类
  10. Type[] t = ass.GetTypes();
  1. //通过程序集的名称反射
  2. Assembly ass = Assembly.Load("ClassLibrary831");
  3. Type t = ass.GetType("ClassLibrary831.NewClass");
  4. object o = Activator.CreateInstance(t, "grayworm", "http://hi.baidu.com/grayworm");
  5. MethodInfo mi = t.GetMethod("show");
  6. mi.Invoke(o, null);
  7. //通过DLL文件全名反射其中的所有类型
  8. Assembly assembly = Assembly.LoadFrom("xxx.dll的路径");
  9. Type[] aa = a.GetTypes();
  10. foreach(Type t in aa)
  11. {
  12. if(t.FullName == "a.b.c")
  13. {
  14. object o = Activator.CreateInstance(t);
  15. }
  16. }

🔚