前言反射可以看透程序的内部构造一什么是反射在 C# 中代码在被编译后会生成一堆“元数据”Metadata。这些元数据记录了这个类叫什么、有哪些属性、有哪些私有方法。常规操作你知道一个类叫Person所以你写new Person()。这叫“编译时”确定。反射操作程序运行的时候你突然拿到一个字符串Person。你想通过这个字符串创建一个对象并调用它里面一个叫SayHello的方法。这时候你就必须用反射。简而言之反射就是在程序【运行时】动态地查看、创建和调用对象的能力。二反射核心“四剑客”System.Reflection命名空间下的这四个核心类类名它是干啥的Type灵魂核心。代表一个类的“蓝图”记录了类的所有信息。PropertyInfo代表类里的属性有 get/set 的那种。MethodInfo代表类里的方法。FieldInfo代表类里的字段私有变量或公有变量。2.1获取构造函数using System; using System.Reflection; public class Program { public static void Main() { // 第一步获取 Person 类的 Type 对象 Type personType typeof(Person); // 第二步获取所有公开的构造函数 ConstructorInfo[] constructors personType.GetConstructors(); Console.WriteLine($找到 {constructors.Length} 个公开的构造函数); foreach (var ctor in constructors) { // 获取构造函数的参数信息 ParameterInfo[] parameters ctor.GetParameters(); Console.WriteLine($- 构造函数名: {ctor.Name}, 参数个数: {parameters.Length}); } } }对象 / 方法作用ctorConstructorInfo 类型。它代表了某一个具体的构造函数比如public Person(int id, string name)。GetParameters()ConstructorInfo 类的方法。作用读取该构造函数的元数据返回一个ParameterInfo数组。ParameterInfo[]参数信息数组。每一个元素对应构造函数中的一个参数里面存着参数的名称、类型、是否有默认值等详细属性。2.2获取无参构造using System; using System.Reflection; Type personType typeof(Person); // 传入空数组代表寻找参数列表为空的构造函数 ConstructorInfo defaultCtor personType.GetConstructor(Type.EmptyTypes); if (defaultCtor ! null) { Console.WriteLine(成功获取到无参构造函数); // 动态执行构造函数相当于执行了 new Person() object personInstance defaultCtor.Invoke(null); Console.WriteLine($对象已创建{personInstance}); }2.3获取有参构造using System; using System.Reflection; Type personType typeof(Person); // 1. 定义参数类型数组顺序必须匹配string, int, int Type[] paramTypes new Type[] { typeof(string), typeof(int), typeof(int) }; // 2. 获取特定的构造函数 ConstructorInfo ctor personType.GetConstructor(paramTypes); if (ctor ! null) { Console.WriteLine(找到了匹配的有参构造函数); // 3. 使用 Invoke 传入具体的参数值来创建对象 object[] parameters new object[] { 小明, 20, 1 }; object personInstance ctor.Invoke(parameters); // 验证结果 Person p (Person)personInstance; Console.WriteLine($实例化成功姓名{p.Name}, 年龄{p.Age}, ID{p.Id}); } else { Console.WriteLine(未找到匹配的构造函数请检查参数类型和顺序。); }三反射的基本操作1.获取Type对象拿到类的说明书// 方式 A通过类名直接拿如果你在写代码时就知道类名 Type t1 typeof(Person); // 方式 B通过对象拿如果你手里已经有个实例了 Person p new Person(); Type t2 p.GetType(); // 方式 C通过字符串拿最强大用于插件化开发或配置文件 Type t3 Type.GetType(MyNamespace.Person);2.动态创建对象2.1语法以前你用new现在你用“魔法”object obj Activator.CreateInstance(t1); // 相当于 new Person()2.2实例using System; // 定义一个普通的类 class Person { public string Name { get; set; } // 构造函数 public Person() { Console.WriteLine(Person 被构造出来了); } } class Program { static void Main() { // 1. 获取 Person 的“类型信息” (这就是 t1) Type type typeof(Person); // 2. 动态创建对象 - 等价于 new Person() // 这里虽然没写 Person但运行时会造出 Person 的实例 object obj Activator.CreateInstance(type); // 3. 由于是 object 类型需要强转才能使用特有属性 Person p obj as Person; if (p ! null) { p.Name 云飞; Console.WriteLine(我动态创建的人叫 p.Name); } } }2.3提示因为动态创建时编译器不知道你造的是啥类型所以统一先装进object所有类的老祖宗里。你后面需要强转回原来的类型比如Person才能用。3.操作属性3.1语法哪怕属性是私有的反射也能强行“潜入”PropertyInfo prop t1.GetProperty(Name); // 给 obj 对象的 Name 属性赋值为 Gemini prop.SetValue(obj, Gemini); // 获取值 string val (string)prop.GetValue(obj);3.2实例using System; using System.Reflection; // 反射必须引用这个命名空间 // 定义一个 Person 类 class Person { // 公开属性 Name public string Name { get; set; } // 私有属性 Age正常代码里外面访问不到 private int Age { get; set; } // 打印信息的方法 public void PrintInfo() { Console.WriteLine($姓名{Name}年龄{Age}); } } class Program { static void Main() { // 1. 获取 Person 类的类型信息t1 Type personType typeof(Person); // 2. 动态创建 Person 对象obj等价于 new Person() object obj Activator.CreateInstance(personType); // 操作公开属性 Name // 3. 拿到 Name 属性的 PropertyInfo万能钥匙 PropertyInfo nameProp personType.GetProperty(Name); // 4. 给 Name 属性赋值等价于 obj.Name Gemini nameProp.SetValue(obj, Gemini); // 5. 从 Name 属性取值等价于 string val obj.Name string nameVal (string)nameProp.GetValue(obj); Console.WriteLine($动态读取 Name{nameVal}); // 【重点】操作私有属性 Age正常代码访问不到 // 注意私有属性需要加 BindingFlags 才能拿到 PropertyInfo ageProp personType.GetProperty(Age, BindingFlags.Instance | BindingFlags.NonPublic); // 给私有 Age 属性赋值正常代码里 obj.Age 是访问不到的 ageProp.SetValue(obj, 25); // 读取私有 Age 属性的值 int ageVal (int)ageProp.GetValue(obj); Console.WriteLine($强行读取私有 Age{ageVal}); // 验证赋值是否成功 ((Person)obj).PrintInfo(); } }3.3说明4.调用方法4.1语法MethodInfo method t1.GetMethod(SayHello); // 参数1哪个对象调 参数2方法需要的参数没有就传 null method.Invoke(obj, null);4.2实例using System; using System.Reflection; // 反射必须引用这个命名空间 // 定义一个 Person 类 class Person { // 公开方法SayHello无参数 public void SayHello() { Console.WriteLine(Hello! 我是Person对象); } // 带参数的公开方法SayHelloTo public void SayHelloTo(string name, int age) { Console.WriteLine($你好{name}我今年{age}岁了); } // 私有方法PrivateSayHi正常代码里外面调用不到 private void PrivateSayHi() { Console.WriteLine(我是私有方法正常代码调用不了反射能强行调用); } } class Program { static void Main() { // 1. 获取 Person 类的类型信息t1 Type personType typeof(Person); // 2. 动态创建 Person 对象obj等价于 new Person() object obj Activator.CreateInstance(personType); // 1. 调用无参数公开方法 SayHello // 3. 拿到 SayHello 方法的 MethodInfo万能遥控器 MethodInfo sayHelloMethod personType.GetMethod(SayHello); // 4. 调用方法等价于 obj.SayHello() sayHelloMethod.Invoke(obj, null); // 无参数传null // 2. 调用带参数的公开方法 SayHelloTo MethodInfo sayHelloToMethod personType.GetMethod(SayHelloTo); // 带参数第二个参数传 object[] 数组把参数放进去 sayHelloToMethod.Invoke(obj, new object[] { 云飞, 24 }); // 【重点】调用私有方法 PrivateSayHi正常代码调用不了 // 注意私有方法需要加 BindingFlags 才能拿到 MethodInfo privateMethod personType.GetMethod(PrivateSayHi, BindingFlags.Instance | BindingFlags.NonPublic); // 强行调用私有方法 privateMethod.Invoke(obj, null); } }4.3说明四反射能干什么 “骚操作”1.查看类的所有成员foreach (var item in t1.GetMembers()) { Console.WriteLine(${item.MemberType} : {item.Name}); }2.访问私有成员2.1语法这是反射最被诟病但也最强大的地方。通过BindingFlags你可以强行访问private变量FieldInfo privateField t1.GetField(_age, BindingFlags.NonPublic | BindingFlags.Instance); privateField.SetValue(obj, 18); // 强行改年龄2.2说明GetField(_age)从类的类型信息里根据名字_age去获取这个字段。BindingFlags.NonPublic | BindingFlags.Instance这是关键参数NonPublic非公开的意思是我要找private或internal的。Instance实例成员意思是找属于对象的变量而不是静态的。总结这句代码就是在说“嘿反射工具给我把那个私有的、属于对象的_age字段拿出来”五反射的优缺点1.优点极度灵活可以在不修改代码的前提下通过配置文件加载不同的逻辑这就是IOC 容器、ORM 框架的底层逻辑。解耦调用方不需要知道实现类的具体类型2.缺点性能损耗反射是动态寻找元数据比直接调用慢的多在高性能循环里不要使用代码安全性反射可以无视访问修饰符可能会破坏结构内部的封装性维护困难报错通常在运行时才发现编辑器帮不了你自己找错误