在 C# 中通过反射给字段赋值是一种动态操作对象成员的常用方法,尤其适用于需要绕过编译时类型检查的场景。以下是完整的实现步骤和代码示例:
反射赋值的核心步骤
- 获取类型信息:通过
Type
类获取目标对象的类型元数据。 - 获取字段信息:通过
GetField
或GetFields
方法获取目标字段。 - 设置字段值:使用
FieldInfo.SetValue
方法为字段赋值。 - 处理类型转换:确保输入值与字段类型兼容(可能需要类型转换)。
完整代码实现
1 | using System; |
关键注意事项
-
字段可见性:
-
公共字段直接使用
GetField("Name")
。 -
私有字段需要指定
BindingFlags
:1
BindingFlags.NonPublic | BindingFlags.Instance
-
-
静态字段:
-
设置静态字段时,
SetValue
的第一个参数传null
:1
countryField.SetValue(null, "USA");
-
-
类型转换:
-
如果字段类型与输入值类型不匹配,需手动转换:
1
Convert.ChangeType(value, targetType);
-
-
性能优化:
-
反射操作较慢,频繁调用时可缓存
FieldInfo
对象:1
private static readonly FieldInfo _cachedAgeField = typeof(Person).GetField(...);
-
-
安全性:
- 修改私有字段可能破坏封装性,需谨慎使用。
- 可通过
fieldInfo.IsInitOnly
检查字段是否为只读。
高级用法:泛型扩展方法
1 | public static class ReflectionExtensions |
适用场景
- 动态配置加载(如将 JSON/XML 数据映射到对象)。
- 实现通用序列化/反序列化工具。
- 编写测试框架时动态修改对象状态。
- 依赖注入容器等底层框架开发。
替代方案
-
表达式树(Expression Trees):
通过编译后的表达式树操作字段,性能接近直接赋值:1
2
3
4
5
6
7
8var param = Expression.Parameter(typeof(Person));
var field = Expression.Field(param, "Name");
var setter = Expression.Lambda<Action<Person, string>>(
Expression.Assign(field, Expression.Convert(paramValue, typeof(string))),
param, paramValue
).Compile();
setter(person, "Bob"); -
dynamic 关键字:
简单场景下快速访问公共字段(但无法访问私有成员):1
2dynamic dynPerson = person;
dynPerson.Name = "Charlie";
通过反射赋值提供了极大的灵活性,但需权衡性能与代码可维护性。在需要高性能的场景,推荐使用预编译的表达式树或代码生成技术(如 Source Generators)。