C# 反射(Reflection)详解
一、什么是反射?
1.1 生活比喻
反射就像是一台X 光机:
平时你看一个人,只能看到外表(调用公开的方法和属性)。
用 X 光机一照,你能看到他的骨骼、器官——连藏在身体里的东西都看得一清二楚。
反射就是程序的"X 光机"——运行时照一照一个类或对象,就能知道:
- 它叫什么名字
- 有哪些属性、方法、字段
- 这些成员是 public 还是 private
- 甚至可以调用 private 方法、修改 private 字段
1.2 一句话理解
反射 = 程序在运行时"照镜子"看自己的能力。 通过反射,代码可以动态地获取类型信息、创建对象、调用方法、访问字段,而不需要在编译时就知道具体类型。
1.3 反射能做什么?
// 不用反射——编译时就确定了
Person p = new Person();
p.Name = "张三";
p.SayHello();
// 用反射——运行时才确定
Type type = Type.GetType("Person"); // 1. 运行时获取类型
object obj = Activator.CreateInstance(type); // 2. 运行时创建对象
PropertyInfo prop = type.GetProperty("Name"); // 3. 运行时获取属性
prop.SetValue(obj, "张三"); // 4. 运行时设置属性
MethodInfo method = type.GetMethod("SayHello");// 5. 运行时获取方法
method.Invoke(obj, null); // 6. 运行时调用方法
二、获取类型信息 —— Type 对象
2.1 三种方式获取 Type
// 方式一:typeof(编译时已知类型)
Type t1 = typeof(string);
Type t2 = typeof(List<int>);
Type t3 = typeof(Program);
// 方式二:对象.GetType()(有实例时)
string s = "hello";
Type t4 = s.GetType();
Person p = new Person();
Type t5 = p.GetType();
// 方式三:Type.GetType()(通过字符串名称,运行时动态)
Type t6 = Type.GetType("System.String"); // 需要完整命名空间
Type t7 = Type.GetType("MyApp.Person"); // 自定义类要写全命名空间
// 方式四:Assembly.GetType()(从程序集中获取)
Type t8 = Assembly.GetExecutingAssembly().GetType("MyApp.Person");
三种方式对比:
| 方式 | 场景 | 示例 |
|---|---|---|
typeof(T) |
编译时就知道类型 | typeof(int) |
obj.GetType() |
有一个对象实例 | "hello".GetType() |
Type.GetType("名字") |
运行时才知道类型名 | Type.GetType("System.String") |
2.2 Type 对象能告诉你什么?
using System;
using System.Reflection;
public class Person
{
public string Name { get; set; }
private int _age;
public Person() { }
public Person(string name) => Name = name;
public void SayHello() => Console.WriteLine($"你好,我是{Name}");
private void Secret() => Console.WriteLine("秘密方法");
}
class Program
{
static void Main()
{
Type type = typeof(Person);
Console.WriteLine($"类名: {type.Name}");
Console.WriteLine($"全名: {type.FullName}");
Console.WriteLine($"命名空间: {type.Namespace}");
Console.WriteLine($"是类吗: {type.IsClass}");
Console.WriteLine($"是公开的吗: {type.IsPublic}");
Console.WriteLine($"基类: {type.BaseType?.Name}");
}
}
输出:
类名: Person
全名: Program+Person
命名空间:
是类吗: True
是公开的吗: True
基类: Object
三、查看类的成员 —— 反射的"扫描"功能
3.1 获取构造函数
Type type = typeof(Person);
ConstructorInfo[] constructors = type.GetConstructors();
foreach (ConstructorInfo ctor in constructors)
{
ParameterInfo[] parameters = ctor.GetParameters();
string paramStr = string.Join(", ",
parameters.Select(p => $"{p.ParameterType.Name} {p.Name}"));
Console.WriteLine($"构造函数: Person({paramStr})");
}
输出:
构造函数: Person()
构造函数: Person(String name)
3.2 获取属性
Type type = typeof(Person);
PropertyInfo[] properties = type.GetProperties(); // 只获取 public 属性
foreach (PropertyInfo prop in properties)
{
Console.WriteLine($"属性: {prop.PropertyType.Name} {prop.Name}");
Console.WriteLine($" 可读: {prop.CanRead}, 可写: {prop.CanWrite}");
}
输出:
属性: String Name
可读: True, 可写: True
// 获取所有属性(包括私有属性)
PropertyInfo[] allProps = type.GetProperties(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
3.3 获取方法
Type type = typeof(Person);
MethodInfo[] methods = type.GetMethods(); // 只获取 public 方法
foreach (MethodInfo method in methods)
{
ParameterInfo[] parameters = method.GetParameters();
string paramStr = string.Join(", ",
parameters.Select(p => $"{p.ParameterType.Name} {p.Name}"));
Console.WriteLine($"{method.ReturnType.Name} {method.Name}({paramStr})");
}
输出:
Void SayHello()
String ToString()
Boolean Equals(Object obj)
Int32 GetHashCode()
Type GetType()
...
// 获取特定方法
MethodInfo sayHello = type.GetMethod("SayHello");
Console.WriteLine($"找到方法: {sayHello?.Name}");
// 获取私有方法
MethodInfo secret = type.GetMethod("Secret",
BindingFlags.NonPublic | BindingFlags.Instance);
Console.WriteLine($"私有方法: {secret?.Name}");
3.4 获取字段
Type type = typeof(Person);
// 获取所有字段(包括私有)
FieldInfo[] fields = type.GetFields(
BindingFlags.NonPublic | BindingFlags.Instance);
foreach (FieldInfo field in fields)
{
Console.WriteLine($"字段: {field.FieldType.Name} {field.Name}");
Console.WriteLine($" 私有: {field.IsPrivate}");
}
输出:
字段: Int32 _age
私有: True
3.5 完整扫描示例
using System;
using System.Reflection;
class Program
{
static void ScanType(Type type)
{
Console.WriteLine($"\n===== {type.Name} 全扫描 =====");
Console.WriteLine($"全名: {type.FullName}");
Console.WriteLine($"基类: {type.BaseType?.Name}");
// 构造函数
Console.WriteLine($"\n--- 构造函数 ({type.GetConstructors().Length}个) ---");
foreach (var ctor in type.GetConstructors())
{
var p = string.Join(", ", ctor.GetParameters().Select(x => $"{x.ParameterType.Name} {x.Name}"));
Console.WriteLine($" {type.Name}({p})");
}
// 属性
var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
Console.WriteLine($"\n--- 属性 ({props.Length}个) ---");
foreach (var prop in props)
{
string access = (prop.CanRead ? "get;" : "") + (prop.CanWrite ? "set;" : "");
string visibility = prop.GetMethod?.IsPublic == true ? "public" : "private";
Console.WriteLine($" {visibility} {prop.PropertyType.Name} {prop.Name} {{ {access} }}");
}
// 方法
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic
| BindingFlags.Instance | BindingFlags.DeclaredOnly);
Console.WriteLine($"\n--- 方法 ({methods.Length}个) ---");
foreach (var method in methods)
{
var p = string.Join(", ", method.GetParameters().Select(x => $"{x.ParameterType.Name} {x.Name}"));
string visibility = method.IsPublic ? "public" : "private";
Console.WriteLine($" {visibility} {method.ReturnType.Name} {method.Name}({p})");
}
// 字段
var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
Console.WriteLine($"\n--- 字段 ({fields.Length}个) ---");
foreach (var field in fields)
{
string visibility = field.IsPublic ? "public" : "private";
Console.WriteLine($" {visibility} {field.FieldType.Name} {field.Name}");
}
}
static void Main()
{
ScanType(typeof(Person));
}
}
四、动态创建对象和调用
4.1 动态创建对象
Type type = typeof(Person);
// 方式一:Activator.CreateInstance(无参构造)
object obj1 = Activator.CreateInstance(type);
Console.WriteLine($"类型: {obj1.GetType().Name}");
// 方式二:指定构造函数参数
object obj2 = Activator.CreateInstance(type, "张三");
Console.WriteLine($"Name: {((Person)obj2).Name}"); // 张三
// 方式三:拿到构造函数再调用
ConstructorInfo ctor = type.GetConstructor(new[] { typeof(string) });
object obj3 = ctor.Invoke(new object[] { "李四" });
// 泛型版本
Person p = Activator.CreateInstance<Person>();
4.2 动态设置属性
Type type = typeof(Person);
object obj = Activator.CreateInstance(type);
// 获取属性
PropertyInfo nameProp = type.GetProperty("Name");
// 设置属性值
nameProp.SetValue(obj, "张三");
Console.WriteLine(nameProp.GetValue(obj)); // 张三
// 动态获取和设置——完整示例
static void SetProperty(object obj, string propName, object value)
{
Type type = obj.GetType();
PropertyInfo prop = type.GetProperty(propName);
if (prop != null && prop.CanWrite)
{
prop.SetValue(obj, value);
Console.WriteLine($"设置 {propName} = {value}");
}
}
static object GetProperty(object obj, string propName)
{
Type type = obj.GetType();
PropertyInfo prop = type.GetProperty(propName);
return prop?.GetValue(obj);
}
4.3 动态调用方法
Type type = typeof(Person);
object obj = Activator.CreateInstance(type);
// 设置 Name
type.GetProperty("Name")?.SetValue(obj, "张三");
// 调用无参方法
MethodInfo sayHello = type.GetMethod("SayHello");
sayHello.Invoke(obj, null); // 输出: 你好,我是张三
// 调用有参方法
MethodInfo setName = type.GetMethod("set_Name"); // 属性的 set 方法
setName.Invoke(obj, new object[] { "李四" });
// 调用私有方法
MethodInfo secret = type.GetMethod("Secret",
BindingFlags.NonPublic | BindingFlags.Instance);
secret.Invoke(obj, null); // 输出: 秘密方法
4.4 动态操作字段
Type type = typeof(Person);
object obj = Activator.CreateInstance(type);
// 获取私有字段
FieldInfo ageField = type.GetField("_age",
BindingFlags.NonPublic | BindingFlags.Instance);
// 设置私有字段
ageField.SetValue(obj, 25);
// 读取私有字段
int age = (int)ageField.GetValue(obj);
Console.WriteLine($"年龄: {age}"); // 25
五、完整的反射使用示例
5.1 通用对象拷贝工具
using System;
using System.Reflection;
public class ObjectCopier
{
// 将一个对象的属性值拷贝到另一个对象
public static void Copy<TSource, TTarget>(TSource source, TTarget target)
{
Type sourceType = typeof(TSource);
Type targetType = typeof(TTarget);
foreach (PropertyInfo sourceProp in sourceType.GetProperties())
{
if (!sourceProp.CanRead) continue;
PropertyInfo targetProp = targetType.GetProperty(sourceProp.Name);
if (targetProp == null || !targetProp.CanWrite) continue;
// 类型必须兼容
if (targetProp.PropertyType.IsAssignableFrom(sourceProp.PropertyType))
{
object value = sourceProp.GetValue(source);
targetProp.SetValue(target, value);
}
}
}
}
// 使用
public class StudentDto
{
public string Name { get; set; }
public int Age { get; set; }
}
public class StudentEntity
{
public string Name { get; set; }
public int Age { get; set; }
public string Grade { get; set; } // 这个不会被拷贝
}
class Program
{
static void Main()
{
var dto = new StudentDto { Name = "张三", Age = 18 };
var entity = new StudentEntity();
ObjectCopier.Copy(dto, entity);
Console.WriteLine($"Name: {entity.Name}"); // 张三
Console.WriteLine($"Age: {entity.Age}"); // 18
Console.WriteLine($"Grade: {entity.Grade}"); // null(没被拷贝)
}
}
5.2 简单的依赖注入容器
using System;
using System.Collections.Generic;
using System.Reflection;
public class SimpleContainer
{
private Dictionary<Type, Type> _mappings = new Dictionary<Type, Type>();
// 注册接口和实现类的映射
public void Register<TInterface, TImplementation>()
{
_mappings[typeof(TInterface)] = typeof(TImplementation);
}
// 解析——创建对象并自动注入依赖
public T Resolve<T>()
{
return (T)Resolve(typeof(T));
}
private object Resolve(Type type)
{
// 如果注册了映射,就用实现类
if (_mappings.ContainsKey(type))
{
type = _mappings[type];
}
// 找到构造函数(选参数最多的那个)
var constructors = type.GetConstructors();
if (constructors.Length == 0)
{
return Activator.CreateInstance(type);
}
var ctor = constructors[0];
var parameters = ctor.GetParameters();
var args = new object[parameters.Length];
// 递归解析每个参数
for (int i = 0; i < parameters.Length; i++)
{
args[i] = Resolve(parameters[i].ParameterType);
}
return ctor.Invoke(args);
}
}
// ===== 使用示例 =====
public interface ILogger
{
void Log(string message);
}
public class ConsoleLogger : ILogger
{
public void Log(string message) => Console.WriteLine($"[日志] {message}");
}
public interface IDataService
{
void Save(string data);
}
public class DataService : IDataService
{
private ILogger _logger;
// 构造函数注入
public DataService(ILogger logger)
{
_logger = logger;
}
public void Save(string data)
{
_logger.Log($"保存数据: {data}");
Console.WriteLine("数据已保存");
}
}
class Program
{
static void Main()
{
Console.WriteLine("===== 简单依赖注入容器 — 反射演示 =====\n");
var container = new SimpleContainer();
container.Register<ILogger, ConsoleLogger>();
container.Register<IDataService, DataService>();
var service = container.Resolve<IDataService>();
service.Save("用户信息");
// 输出:
// [日志] 保存数据: 用户信息
// 数据已保存
}
}
5.3 动态调用——简易插件系统
using System;
using System.Reflection;
// 插件接口
public interface IPlugin
{
string Name { get; }
void Execute();
}
// 内置插件
public class HelloPlugin : IPlugin
{
public string Name => "Hello插件";
public void Execute() => Console.WriteLine("Hello World!");
}
public class TimePlugin : IPlugin
{
public string Name => "时间插件";
public void Execute() => Console.WriteLine($"现在时间: {DateTime.Now}");
}
class Program
{
static void Main()
{
Console.WriteLine("===== 简易插件系统 — 反射演示 =====\n");
// 扫描程序集,找到所有 IPlugin 的实现
Assembly assembly = Assembly.GetExecutingAssembly();
Type pluginInterface = typeof(IPlugin);
var pluginTypes = assembly.GetTypes()
.Where(t => pluginInterface.IsAssignableFrom(t) && t != pluginInterface);
Console.WriteLine($"找到 {pluginTypes.Count()} 个插件:\n");
foreach (Type type in pluginTypes)
{
// 动态创建插件实例
IPlugin plugin = (IPlugin)Activator.CreateInstance(type);
Console.WriteLine($" 📦 {plugin.Name}");
Console.Write($" ");
plugin.Execute();
Console.WriteLine();
}
}
}
输出:
===== 简易插件系统 — 反射演示 =====
找到 2 个插件:
📦 Hello插件
Hello World!
📦 时间插件
现在时间: 2026-06-29 14:30:00
六、Assembly —— 程序集的反射
// 获取当前程序集
Assembly assembly = Assembly.GetExecutingAssembly();
// 获取程序集中的所有类型
Type[] allTypes = assembly.GetTypes();
Console.WriteLine($"程序集包含 {allTypes.Length} 个类型:");
foreach (Type type in allTypes.Take(5)) // 只显示前 5 个
{
Console.WriteLine($" {type.FullName}");
}
// 从文件加载程序集
Assembly externalAssembly = Assembly.LoadFrom("D:\\MyLibrary.dll");
Type[] externalTypes = externalAssembly.GetTypes();
// 获取程序集信息
Console.WriteLine($"\n程序集名称: {assembly.GetName().Name}");
Console.WriteLine($"版本: {assembly.GetName().Version}");
Console.WriteLine($"位置: {assembly.Location}");
七、BindingFlags —— 控制反射搜索范围
// BindingFlags 决定了反射能找到什么样的成员
// 只看 public 的(默认)
type.GetMethods(); // 等价于 type.GetMethods(BindingFlags.Public | BindingFlags.Instance)
// 看 public + private
type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
// 只看到静态的
type.GetMethods(BindingFlags.Public | BindingFlags.Static);
// 全看——public + private + static + instance
type.GetMethods(
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Static | BindingFlags.Instance);
| BindingFlags | 含义 |
|---|---|
Public |
公开成员 |
NonPublic |
私有、受保护成员 |
Static |
静态成员 |
Instance |
实例成员 |
DeclaredOnly |
只看本类定义的,不看继承的 |
如果要用 NonPublic 或 Static,必须同时明确指定 Public 或 Instance、NonPublic 等。
只写BindingFlags.NonPublic是找不到任何东西的!
八、常见易错点(避坑指南)
坑1:BindingFlags 必须组合使用
// ❌ 只写 NonPublic,找不到任何东西!
var methods = type.GetMethods(BindingFlags.NonPublic); // 返回空!
// ✅ 必须同时指定 NonPublic 和 Instance(或 Static)
var methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance);
坑2:反射有性能开销
// ❌ 频繁反射——每次都要走元数据
for (int i = 0; i < 10000; i++)
{
Type type = typeof(Person);
PropertyInfo prop = type.GetProperty("Name");
prop.SetValue(obj, "张三");
}
// ✅ 缓存 MemberInfo 对象
PropertyInfo prop = typeof(Person).GetProperty("Name"); // 只获取一次
for (int i = 0; i < 10000; i++)
{
prop.SetValue(obj, "张三"); // 用缓存的 prop
}
坑3:GetType 找不到类型
// Type.GetType 通过字符串找类型时,必须包含命名空间
Type t1 = Type.GetType("Person"); // ❌ 找不到!缺少命名空间
Type t2 = Type.GetType("MyApp.Models.Person"); // ✅ 完整命名空间
// 如果是顶级类用 "Program+Person" 格式
// 推荐用 typeof,安全且编译时检查
Type t3 = typeof(Person); // ✅
坑4:反射调用方法时参数不匹配
// 假设方法签名是: void SetInfo(string name, int age)
MethodInfo method = type.GetMethod("SetInfo");
// ❌ 参数类型不匹配
// method.Invoke(obj, new object[] { "张三", "18" }); // 18 是 string,不是 int
// ✅ 正确
method.Invoke(obj, new object[] { "张三", 18 });
坑5:修改私有成员——可以做到但慎用
// 反射可以突破封装,访问私有成员
FieldInfo privateField = typeof(SecretClass).GetField("_password",
BindingFlags.NonPublic | BindingFlags.Instance);
privateField.SetValue(obj, "新密码"); // 能改,但破坏了封装
// 仅在测试或特殊场景使用,不要滥用
坑6:Invoke 方法比直接调用慢约 100~1000 倍
// 调用次数多的话,考虑用委托缓存加速
MethodInfo method = typeof(Calculator).GetMethod("Add");
var addDelegate = (Func<Calculator, int, int, int>)Delegate.CreateDelegate(
typeof(Func<Calculator, int, int, int>), method);
// 以后就调用委托,比 Invoke 快很多
int result = addDelegate(calc, 3, 5);
九、总结
反射能做什么
| 能力 | 核心技术 |
|---|---|
| 获取类型信息 | typeof(T) / obj.GetType() / Type.GetType() |
| 查看成员列表 | GetProperties(), GetMethods(), GetFields() |
| 创建对象 | Activator.CreateInstance(type) |
| 调用方法 | MethodInfo.Invoke(obj, args) |
| 读写属性 | PropertyInfo.SetValue() / GetValue() |
| 读写字段 | FieldInfo.SetValue() / GetValue() |
| 调用私有成员 | BindingFlags.NonPublic |
反射的应用场景
| 场景 | 说明 |
|---|---|
| ORM 框架 | Entity Framework、Dapper 通过反射将数据库行映射到对象 |
| 依赖注入 | 框架自动分析构造函数参数并创建对象 |
| 序列化 | JSON/XML 序列化器通过反射读写属性和字段 |
| 单元测试 | 测试框架通过反射找到 [TestMethod] 标记的方法 |
| 特性读取 | 通过反射获取 [Attribute] 信息 |
| 插件系统 | 动态加载外部 dll 并创建对象 |
记忆口诀
反射就像 X 光机,照一照类就知道
属性方法都看见,私有的也藏不了
typeof 获取静态型,GetType 实例来取型
Activator 创对象,Invoke 调用方法名
GetProperties 看属性,GetMethods 看方法
GetFields 看字段,SetValue 给它们赋值
性能开销要注意,多缓存来少反射
BindingFlags 别忘组合,私有静态要写明
一句话总结:反射是 C# 运行时查看和操作类型的机制——通过
Type对象获取类的所有信息,通过Activator动态创建对象,通过MethodInfo/PropertyInfo/FieldInfo动态调用、读写成员。它是 ORM、依赖注入、序列化等框架的基石。性能有开销,使用时注意缓存。