无法用GetField直接获取私有字段,必须显式传入BindingFlags.NonPublic | BindingFlags.Instance(静态字段用BindingFlags.Static),缺一不可;否则返回null。

如何用 GetField 获取私有字段(含 BindingFlags.NonPublic 必须项)
直接调用 GetField 默认查不到私有字段,必须显式传入 BindingFlags.NonPublic | BindingFlags.Instance(静态字段则用 BindingFlags.Static)。漏掉任一 flag 都会返回 null,这是最常踩的坑。
常见错误现象:fieldInfo == null 却没检查 flag 是否完整;或误用 GetFields()(它默认只返回 public 字段,即使加了 flag 也不生效,必须用单参数 GetField(string name))。
实操建议:
- 先确认字段名拼写(C# 编译后私有字段名一般不变,但若启用了代码混淆,字段名可能被重命名)
- 确保目标类型已加载且非泛型未构造(如
typeof(List不能直接反射,要) typeof(List)) - 若字段是嵌套类/结构体中的私有字段,需逐层反射,不能跨级直达
用 FieldInfo.SetValue 修改私有字段值(绕过 readonly 和 init-only)
SetValue 能修改大多数私有字段,包括被 readonly 修饰的字段(.NET Core 3.0+ / .NET 5+ 默认允许,旧版需额外设置 fieldInfo.IsInitOnly == false 判断);但对 init 属性的 backing field(编译器生成的 ),只要不是 init 字段本身,仍可改。
注意:修改第三方 DLL 中的字段前,务必确认该字段不被内部缓存、校验或懒加载逻辑依赖——否则可能引发后续行为异常,比如配置类改了但缓存没清,实际运行仍用旧值。
实操建议:
- 修改前先用
GetValue读取原值,便于出错回滚 - 若字段类型是
struct,SetValue会复制值,不影响原实例(除非你操作的是装箱后的对象) - 对泛型类型中的私有字段,
FieldInfo的FieldType返回的是开放泛型(如T),需用MakeGenericType构造具体类型再处理
反射第三方 DLL 时遇到 FieldAccessException 怎么办
这个异常通常不是因为字段私有,而是 DLL 被强签名(Strong-Named)且当前程序集没权限反射非 public 成员——尤其在 .NET Framework 下,默认启用 SecurityTransparent 级别限制。
解决路径取决于运行环境:
- .NET Core / .NET 5+:默认无此限制,只要能加载 DLL 就可反射私有成员(但需注意 AOT 或 trimming 可能移除私有字段元数据)
- .NET Framework:需在入口程序集添加
[assembly: SecurityRules(SecurityRuleSet.Level2, SkipVerificationInFullTrust = true)],或改用ReflectionContext(极少见且复杂,不推荐) - 若 DLL 是 Unity IL2CPP 导出或经 ConfuserEx 等混淆,字段可能已被重命名或元数据擦除,此时反射必然失败
一个快速验证方式:用 ildasm 打开 DLL,查看目标字段是否仍在 .field private 区域存在。
修改 internal 字段要不要加 BindingFlags.NonPublic
要。C# 的 internal 对反射来说等同于私有(即非 public),必须加 BindingFlags.NonPublic 才能获取。很多人误以为 internal 是“模块内可见”,所以反射也应该可见,但反射的可见性判断只认 public / 非 public 两级。
另外注意:如果第三方 DLL 和你的程序集不在同一 InternalsVisibleTo 白名单里,即使加了 flag,.NET 仍可能因程序集边界限制拒绝访问(尤其在 .NET Standard 2.0+ 的某些实现中)。此时唯一可靠办法是用源码编译或 patch DLL(如使用 dnlib)。
真正容易被忽略的一点:字段所在的类本身可能是 internal 的——这时你连 typeof(ClassName) 都拿不到,得先通过 Assembly.GetTypes() 枚举再用名称匹配,再过滤 IsClass && !IsPublic。











