反射访问私有字段需显式指定 bindingflags.nonpublic | bindingflags.instance(或 static),否则无法获取;读写前须校验对象非 null 且字段可访问,readonly/init 字段修改行为未定义;aot 发布需 rd.xml 保留,性能差且破坏封装,应优先重构设计。

反射能访问私有字段,但默认不行——必须显式开启 BindingFlags.NonPublic,否则返回 null 或抛出 FieldNotFoundException。
用 GetField 获取私有字段时必须传 BindingFlags.NonPublic | BindingFlags.Instance
私有实例字段不会被 GetField("name") 找到,哪怕字段确实存在。.NET 默认只查 public 成员。
- 只传
BindingFlags.NonPublic不够,还得指定BindingFlags.Instance(或BindingFlags.Static),否则查不到实例字段 - 如果字段是
static readonly,得用BindingFlags.NonPublic | BindingFlags.Static - 忽略大小写?加
BindingFlags.IgnoreCase,但不推荐——私有字段名通常无歧义,且大小写敏感更安全
示例:
var field = obj.GetType().GetField("_cache", BindingFlags.NonPublic | BindingFlags.Instance);
用 GetValue 读取私有字段前要确认对象非 null 且字段可访问
GetValue 对私有字段有效,但两个常见失败点:目标对象为 null(对实例字段),或字段本身被标记为 init / readonly 且已初始化(部分运行时版本会拒绝读取)。
- 对
null实例调用GetValue私有实例字段 → 抛TargetException -
readonly字段在构造后仍可读取,但 .NET 6+ 对init字段可能限制更严,建议仅用于调试/测试场景 - 若字段是嵌套类型(如
private List<string> _items</string>),GetValue返回的是实际对象引用,可直接操作
设置私有字段值要用 SetValue,但 readonly 字段需额外绕过验证
SetValue 默认拒绝写入 readonly 字段,即使用了 NonPublic。真要改,得先用 FieldInfo.SetValue 配合 RuntimeHelpers.InitializeArray 等底层手段——但这是高危操作,多数情况不该做。
- 普通私有字段:直接
field.SetValue(obj, newValue)即可 -
readonly字段:.NET Core 3.1+ 允许通过反射修改,但行为未定义;.NET Framework 下基本不可写 - 结构体(
struct)的私有字段:SetValue会复制整个 struct,原变量不变——必须用ref参数或装箱后再改
性能与兼容性:反射读写私有字段比直接访问慢 50–100 倍,且受 AOT 编译限制
发布为 NativeAOT(如 .NET 7+ 的 publish -p:PublishAot=true)时,未显式保留的私有字段会被剪裁掉,GetField 直接返回 null,且无运行时提示。
- 热路径(如循环内、高频调用)绝对不要用反射访问私有字段
- AOT 场景下,必须在
rd.xml中声明保留:<type name="MyType" dynamic="true"></type> - 单元测试中用反射访问私有成员,比用
[InternalsVisibleTo]更脆弱——类型重命名或字段重构会静默失效
真正难的不是语法,是判断“该不该用”——私有即契约,绕过它往往意味着设计需要调整,而不是反射需要写得更熟。











