[C.C++] C# 反射(Reflection)超详细解析

999 0
Honkers 2025-8-4 00:15:07 来自手机 | 显示全部楼层 |阅读模式

写在前面的话

我创建的Unity、C#交流群,有兴趣可加入大家一起学习:952914223

0.应用程序结构

应用程序结构:

1.反射概念

反射是.NET框架提供的一个功能强大的机制,它允许程序在运行时检查和操作对象的类型信息。通过使用反射,程序可以动态地创建对象、调用方法、访问字段和属性,无需在编译时显式知道类型信息。在.NET中,所有类型的信息最终都是存储在元数据中的。反射就是.NET提供的一组API,允许我们在运行时访问这些元数据,从而获得关于程序集、模块、类型、成员等的详细信息。

反射概念图:

2.反射用途

System.Reflection 命名空间下

作用
Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。
MethodInfo了解方法的名称、返回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法来调用特定的方法。
PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。
FieldInfo了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。
MemberInfo用于获取有关类 (构造函数、事件、字段、方法和属性) 的所有成员的信息。此类引入所有成员提供的基本功能。
EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。
ConstructorInfo了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetConstructors或 GetConstructor方法来调用特定的构造函数。
ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。

3.System.Type 类

通过这个类可以访问任何给定数据类型的信息。 System.Type 类对于反射起着核心的作用。但它是一个抽象的基类,Type有与每种数据类型对应的派生类,我们使用这个派生类的对象的方法、字段、属性来查找有关该类型的所有信息。
生成Type对象:

  • 使用typeof运算符
  1. // typeof(x)中的x,必须是具体的类名、类型名称等,不可以是变量名称。
  2. Type t = typeof(int);
复制代码
  • 使用GetType()方法
  1. // GetType()方法继承自Object,所以C#中任何对象都具有GetType()方法
  2. int i;
  3. Type t = i.GetType();
复制代码
  • 使用Type类的静态方法GetType()
  1. Type t = Type.GetType("System.Double");
复制代码

属性:

属性描述
Name数据类型名
FullName数据类型的完全限定名(包括命名空间名)
Namespace定义数据类型的命名空间名
IsAbstract指示该类型是否是抽象类型
IsArray指示该类型是否是数组
IsClass指示该类型是否是类
IsEnum指示该类型是否是枚举
IsInterface指示该类型是否是接口
IsPublic指示该类型是否是公有的
IsSealed指示该类型是否是密封类
IsValueType指示该类型是否是值类型

方法:

方法描述
GetConstructor(),GetConstructors()返回ConstructorInfo类型,用于取得该类的构造函数的信息
GetEvent(), GetEvents()返回EventInfo类型,用于取得该类的事件的信息
GetField(), GetFields()返回FieldInfo类型,用于取得该类的字段(成员变量)的信息
GetInterface(),GetInterfaces()返回InterfaceInfo类型,用于取得该类实现的接口的信息
GetMember(), GetMembers()返回MemberInfo类型,用于取得该类的所有成员的信息
GetMethod(), GetMethods()返回MethodInfo类型,用于取得该类的方法的信息
GetProperty(), GetProperties()返回PropertyInfo类型,用于取得该类的属性的信息

4.System.Reflection.Assembly 类

方法描述
Load(String)通过程序集名称返回Assembly对象
LoadFrom(String)通过DLL文件名称返回Assembly对象
LoadFile(String)加载指定路径上的程序集文件的内容返回Assembly对象。
GetType(String)通过Assembly获取程序集中类,参数必须是类的全名
GetTypes通过Assembly获取程序集中所有的类
  1. // 通过程序集名称返回Assembly对象
  2. Assembly ass = Assembly.Load("MyClassLibrary");
  3. // 通过DLL文件名称返回Assembly对象
  4. Assembly ass = Assembly.LoadFrom("MyClassLibrary.dll");
  5. // 通过Assembly获取程序集中类
  6. Type t = ass.GetType("MyClassLibrary.NewClass"); //参数必须是类的全名
  7. // 通过Assembly获取程序集中所有的类
  8. Type[] t = ass.GetTypes();
复制代码

5.BindingFlags 指定反射查找范围

默认值:

  1. // 默认值
  2. Default
复制代码

查找:
这些标记用于反射的时候查找类型成员:

  1. // 表示查找的时候,需要忽略大小写。
  2. IgnoreCase
  3. // 仅查找此特定类型中声明的成员,而不会包括这个类继承得到的成员。
  4. DeclaredOnly
  5. // 仅查找类型中的实例成员。
  6. Instance
  7. // 仅查找类型中的静态成员。
  8. Static
  9. // 仅查找类型中的公共成员。
  10. Public
  11. // 仅查找类型中的非公共成员(internal protected private)
  12. NonPublic
  13. // 会查找此特定类型继承树上得到的静态成员。但仅继承公共(public)静态成员和受保护(protected)静态成员;不包含私有静态成员,也不包含嵌套类型。
  14. FlattenHierarchy
复制代码

调用:
这些标记用于为InvokeMember方法提供参数,告知应该如何反射调用一个方法:

  1. // 调用方法。
  2. InvokeMethod
  3. // 创建实例。
  4. CreateInstance
  5. // 获取字段的值。
  6. GetField
  7. // 设置字段的值。
  8. SetField
  9. // 获取属性的值。
  10. GetProperty
  11. // 设置属性的值。
  12. SetProperty
复制代码

常用的组合:

  • 拿到所有成员
  1. BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance
复制代码
  • 拿到公有的实例成员:
  1. BindingFlags.Public | BindingFlags.Instance
复制代码

6.使用案例

People 类:

  1. namespace Ming
  2. {
  3. public class People
  4. {
  5. string name;
  6. int age;
  7. public string Name
  8. {
  9. get => name;
  10. set => name = value;
  11. }
  12. public int Age
  13. {
  14. get => age;
  15. set => age = value;
  16. }
  17. public People(string name, int age)
  18. {
  19. this.name = name;
  20. this.age = age;
  21. }
  22. public void SayHello()
  23. {
  24. Console.Writeline($"{name} say:Hello");
  25. }
  26. }
  27. }
复制代码

6.1 获取类的名字、命名空间、程序集

  1. //类的名称
  2. string name = peopleType.Name;
  3. Console.WriteLine(name);
  4. //类的命名空间
  5. string space = peopleType.Namespace;
  6. Console.WriteLine(space);
  7. //类的程序集
  8. Assembly assembly = peopleType.Assembly;
  9. Console.WriteLine(assembly);
  10. /*
  11. People
  12. Ming
  13. DesignPattern, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
  14. */
复制代码

6.2 获取/修改类的字段、属性、方法、构造函数、事件等

  1. BindingFlags flag = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance;
  2. People people = new People("ming", 18);
  3. // 获取Type对象
  4. Type peopleType = people.GetType();
  5. // 获取name字段值
  6. Console.WriteLine("------获取name字段值------");
  7. FieldInfo nameField = peopleType.GetField("name", flag);
  8. Console.WriteLine(nameField.GetValue(people));
  9. // 设置name字段
  10. Console.WriteLine("------设置name字段------");
  11. nameField.SetValue(people, "ming123");
  12. Console.WriteLine(nameField.GetValue(people));
  13. // 获取所有字段
  14. Console.WriteLine("------获取所有字段------");
  15. FieldInfo[] fieldInfos = peopleType.GetFields(flag);
  16. foreach (var field in fieldInfos)
  17. {
  18. Console.WriteLine($"{field.Name}: {field.GetValue(people)}");
  19. }
  20. // 获取所有属性
  21. Console.WriteLine("------获取所有属性------");
  22. PropertyInfo[] propertyInfos = peopleType.GetProperties(flag);
  23. foreach (var property in propertyInfos)
  24. {
  25. Console.WriteLine($"{property.Name}: {property.GetValue(people)}");
  26. }
  27. // 获取所有方法
  28. Console.WriteLine("------获取所有方法------");
  29. MethodInfo[] methodInfos = peopleType.GetMethods(BindingFlags.Instance | BindingFlags.Public);
  30. foreach (var method in methodInfos)
  31. {
  32. Console.WriteLine($"{method.Name}");
  33. }
  34. // 查看类构造函数
  35. ConstructorInfo[] constructorInfos = peopleType.GetConstructors();
  36. foreach (ConstructorInfo constructor in constructorInfos)
  37. {
  38. ParameterInfo[] ps = constructor.GetParameters(); //取出每个构造函数的所有参数
  39. foreach (ParameterInfo pi in ps)
  40. {
  41. Console.Write(pi.ParameterType.ToString() + " " + pi.Name + ",");
  42. }
  43. }
  44. /*
  45. ------获取name字段值------
  46. ming
  47. ------设置name字段------
  48. ming123
  49. ------获取所有字段------
  50. name: ming123
  51. age: 18
  52. ------获取所有属性------
  53. Name: ming123
  54. Age: 18
  55. ------获取所有方法------
  56. get_Name
  57. set_Name
  58. get_Age
  59. set_Age
  60. SayHello
  61. GetType
  62. ToString
  63. Equals
  64. GetHashCode
  65. ------查看类构造函数------
  66. System.String name,System.Int32 age,
  67. */
复制代码

6.3 方法调用

6.3.1 调用无参函数

  1. // 调用无参方法
  2. MethodInfo method1 = peopleType.GetMethod("SayHello");
  3. Console.WriteLine($"方法名:{method1.Name}");
  4. Console.WriteLine($"返回值:{method1.ReturnType}");
  5. method1.Invoke(people, null);
  6. /*
  7. 方法名:SayHello
  8. 返回值:System.Void
  9. ming123 say: Hello
  10. */
复制代码

6.3.2 调用有参函数

People 新增方法

  1. public int Sum(int a, int b)
  2. {
  3. return a + b;
  4. }
复制代码

调用

  1. MethodInfo method2 = peopleType.GetMethod("Sum");
  2. Console.WriteLine($"方法名:{method2.Name}");
  3. Console.WriteLine($"返回值:{method2.ReturnType}");
  4. foreach (var item in method2.GetParameters())
  5. {
  6. Console.WriteLine($"参数名:{item.Name}");
  7. Console.WriteLine($"参数类型:{item.ParameterType}");
  8. }
  9. var sum = method2.Invoke(people, new object[]{1, 2});
  10. Console.WriteLine(sum);
复制代码

结果

  1. 方法名:Sum
  2. 返回值:System.Int32
  3. 参数名:a
  4. 参数类型:System.Int32
  5. 参数名:b
  6. 参数类型:System.Int32
  7. 3
复制代码

6.3.3 调用私有方法

People 新增方法

  1. private void PrintAge()
  2. {
  3. Console.WriteLine(age);
  4. }
复制代码

调用

  1. MethodInfo method3 = peopleType.GetMethod("PrintAge", BindingFlags.NonPublic | BindingFlags.Instance);
  2. method3.Invoke(people, null);
复制代码

结果

  1. 18
复制代码

6.3.4 调用泛型方法People 新增方法

People 新增方法

  1. public void GenericsMethod<T>(T parm)
  2. {
  3. Console.WriteLine($"类型: {typeof(T)},值: {parm}");
  4. }
复制代码

调用

  1. MethodInfo method4 = peopleType.GetMethod("GenericsMethod").MakeGenericMethod(new Type[]{typeof(string)});
  2. MethodInfo method5 = peopleType.GetMethod("GenericsMethod").MakeGenericMethod(new Type[]{typeof(int)});
  3. method4.Invoke(people, new object[]{"sting泛型"});
  4. method5.Invoke(people, new object[]{123});
复制代码

结果

  1. 类型: System.String,值: sting泛型
  2. 类型: System.Int32,值: 123
复制代码

6.3.5 调用带有输出参数的方法(ref, out)

People 新增方法

  1. public bool TryParse(string input, out int result)
  2. {
  3. return int.TryParse(input, out result);
  4. }
复制代码

调用

  1. MethodInfo method6 = peopleType.GetMethod("TryParse");
  2. // 创建参数数组,包括输入和输出参数
  3. object[] parameters = new object[] { "123", null };
  4. // 调用方法
  5. var success = (bool)method6.Invoke(people, parameters);
  6. // 获取输出参数的值
  7. int parsedValue = (int)parameters[1];
  8. if (success)
  9. Console.WriteLine($"Parsing successful: {parsedValue}");
  10. else
  11. Console.WriteLine("Parsing failed.");
复制代码

结果

  1. Parsing successful: 123
复制代码

6.3.6 调用重载方法

People 新增方法

  1. public int Add(int a, int b)
  2. {
  3. return a + b;
  4. }
  5. public double Add(double a, double b)
  6. {
  7. return a + b;
  8. }
复制代码

调用

  1. // 指定要调用的重载方法的参数类型
  2. Type[] paramTypes1 = { typeof(int), typeof(int) };
  3. Type[] paramTypes2 = { typeof(double), typeof(double) };
  4. MethodInfo method7 = peopleType.GetMethod("Add", paramTypes1);
  5. MethodInfo method8 = peopleType.GetMethod("Add", paramTypes2);
  6. // 调用重载方法
  7. var result1 = method7.Invoke(people, new object[] { 10, 20 });
  8. var result2 = method8.Invoke(people, new object[] { 5.5, 6.5 });
  9. Console.WriteLine($"10 + 20 = {result1}");
  10. Console.WriteLine($"5.5 + 6.5 = {result2}");
复制代码

结果

  1. 10 + 20 = 30
  2. 5.5 + 6.5 = 12
复制代码

6.4 动态生成对象

6.4.1 用构造函数动态生成对象

  1. // 根据参数类型获取构造函数
  2. ConstructorInfo constructor1 = peopleType.GetConstructor(new Type[]{ typeof(string), typeof(int) });
  3. // 构造Object数组,作为构造函数的输入参数
  4. object[] obj = new object[2]{ "ming111", 22 };
  5. // 调用构造函数生成对象
  6. object o = constructor1.Invoke(obj);
  7. // 调用生成的对象的方法测试是否对象生成成功
  8. ((People)o).SayHello();
复制代码

结果

  1. ming111 say: Hello
复制代码

6.4.2 用Activator生成对象

  1. object obj1 = Activator.CreateInstance(peopleType, "ming222", 25);
  2. ((People)obj1).SayHello();
复制代码

结果

  1. ming222 say: Hello
复制代码

7.反射总结

  • 优点:
    反射是C#中一个非常强大的特性是C#高级编程中不可或缺的一部分,了解和掌握反射的使用可以帮助开发者编写更加灵活和强大的.NET应用程序。它提供了一种在运行时查询和操作类型信息的能力,通过反射,我们可以动态地创建对象、调用方法、访问字段和属性,这为编写灵活和动态的代码提供了极大的便利。
  • 缺点:
    尽管反射提供了强大的功能,但它也有一些缺点。反射操作通常比直接代码调用要慢,因为它需要在运行时解析类型信息。此外,过度使用反射可能会使代码变得难以理解和维护。因此,我们应该谨慎使用,在使用反射时应该权衡其给项目带来的好处和成本,避免不必要的性能开销和复杂性增加。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2026 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行