C# 中的反射(Reflection)是 .NET 提供的一种强大机制,允许程序在运行时检查、分析和操作类型(类、结构、接口、方法、属性等)的元数据,并动态创建对象、调用方法、访问字段或属性。它主要通过System.Reflection命名空间中的类实现。
一、反射的核心用途
- 动态加载程序集(Assembly)
- 获取类型信息(Type)
- 创建对象实例(Activator / ConstructorInfo)
- 调用方法(MethodInfo.Invoke)
- 读写属性或字段(PropertyInfo / FieldInfo)
- 检查特性(Attribute)
- 实现插件系统、ORM、序列化框架等
二、基础使用步骤
1. 获取 Type 对象
// 方法1:通过 typeofTypetype1=typeof(string);// 方法2:通过对象的 GetType()stringstr="hello";Typetype2=str.GetType();// 方法3:通过类型全名从 Assembly 加载Typetype3=Type.GetType("System.String");// 方法4:从程序集加载Assemblyassembly=Assembly.LoadFrom("MyLibrary.dll");Typetype4=assembly.GetType("MyNamespace.MyClass");注意:
Type.GetType("...")只能加载当前应用域中已加载或 mscorlib/System 中的类型,跨程序集需指定完整名称(含 Assembly 信息)。
2. 创建对象实例
使用Activator.CreateInstance
Typetype=typeof(Person);objectobj=Activator.CreateInstance(type);// 调用无参构造函数// 带参数构造objectobj2=Activator.CreateInstance(type,"Alice",30);使用ConstructorInfo
ConstructorInfoctor=type.GetConstructor(newType[]{typeof(string),typeof(int)});objectinstance=ctor.Invoke(newobject[]{"Bob",25});3. 调用方法
Typetype=typeof(Calculator);objectcalc=Activator.CreateInstance(type);// 获取方法MethodInfomethod=type.GetMethod("Add");// 调用实例方法intresult=(int)method.Invoke(calc,newobject[]{10,20});// 调用静态方法MethodInfostaticMethod=type.GetMethod("Multiply");intstaticResult=(int)staticMethod.Invoke(null,newobject[]{5,6});如果方法是泛型,需先调用
MakeGenericMethod()。
4. 访问属性(Property)
Typetype=typeof(Person);objectperson=Activator.CreateInstance(type);// 设置属性PropertyInfonameProp=type.GetProperty("Name");nameProp.SetValue(person,"Charlie");// 获取属性值stringname=(string)nameProp.GetValue(person);5. 访问字段(Field)
FieldInfofield=type.GetField("_age",BindingFlags.NonPublic|BindingFlags.Instance);field.SetValue(person,28);intage=(int)field.GetValue(person);注意:私有成员需要
BindingFlags.NonPublic。
6. 获取所有成员
Typetype=typeof(MyClass);// 所有公共方法MethodInfo[]methods=type.GetMethods();// 所有公共属性PropertyInfo[]props=type.GetProperties();// 所有字段(包括私有)FieldInfo[]fields=type.GetFields(BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance);// 所有构造函数ConstructorInfo[]ctors=type.GetConstructors();7. 检查和读取特性(Attribute)
[DisplayName("用户实体")]publicclassUser{}// 检查是否有某个 AttributeboolhasAttr=type.IsDefined(typeof(DisplayNameAttribute),false);// 获取 Attribute 实例varattr=(DisplayNameAttribute)type.GetCustomAttribute(typeof(DisplayNameAttribute));Console.WriteLine(attr.DisplayName);// 输出:"用户实体"三、常用 BindingFlags 枚举值
| 标志 | 说明 |
|---|---|
Public | 公共成员(默认) |
NonPublic | 私有/受保护成员 |
Instance | 实例成员 |
Static | 静态成员 |
DeclaredOnly | 仅当前类型声明的成员(不包括继承) |
示例:
type.GetMethod("PrivateMethod",BindingFlags.NonPublic|BindingFlags.Instance);四、性能注意事项
- 反射比直接调用慢很多(可能慢 10~100 倍),因为涉及类型查找、安全检查、装箱/拆箱等。
- 避免在高频循环中使用反射。
- 可缓存 MethodInfo / PropertyInfo 等以提升性能。
- 考虑使用委托(如
Delegate.CreateDelegate)或表达式树(Expression)优化。 - .NET Core / .NET 5+ 引入了
System.Reflection.Emit和source generator等更高效替代方案。
五、实际应用场景举例
1. 通用对象拷贝器
publicstaticvoidCopyProperties(objectsource,objecttarget){varsourceType=source.GetType();vartargetType=target.GetType();foreach(varpropinsourceType.GetProperties()){vartargetProp=targetType.GetProperty(prop.Name);if(targetProp!=null&&targetProp.CanWrite){targetProp.SetValue(target,prop.GetValue(source));}}}2. 插件系统
Assemblyplugin=Assembly.LoadFrom("Plugin.dll");TypepluginType=plugin.GetTypes().FirstOrDefault(t=>typeof(IPlugin).IsAssignableFrom(t));IPlugininstance=(IPlugin)Activator.CreateInstance(pluginType);instance.Execute();3. ORM 映射(如将 DataTable 转为对象)
publicstaticTToObject<T>(DataRowrow)whereT:new(){Tobj=newT();Typetype=typeof(T);foreach(DataColumncolinrow.Table.Columns){PropertyInfoprop=type.GetProperty(col.ColumnName);if(prop!=null&&row[col]!=DBNull.Value)prop.SetValue(obj,row[col]);}returnobj;}六、总结
| 优点 | 缺点 |
|---|---|
| 高度灵活,支持运行时动态行为 | 性能开销大 |
| 实现通用框架(如 DI、AOP、序列化) | 代码可读性降低 |
| 支持插件、脚本扩展 | 容易引发运行时异常(如拼写错误) |
| 可用于调试、测试工具 | 不支持 AOT 编译(如 Native AOT)的部分场景 |
✅建议:仅在必要时使用反射,并做好异常处理(如
NullReferenceException、TargetException、MissingMethodException等)。