C#反射机制详解:从窥视成员到获取和修改私有数据
文章详细解析了C#中的反射技术,涵盖如何使用GetMembers配合BindingFlags全面检查类结构,以及如何利用FieldInfo、PropertyInfo和MethodInfo突破封装,实现读取和写入私有字段的真实值。
结果或目标
掌握如何在.NET环境中使用反射机制,不仅能在程序运行时查看类的内部构造(包括私有字段、属性和方法),还能在不改变源代码的情况下,动态地获取甚至篡改类的私有状态。
准备条件
本文假设读者具备C#基础语法知识,并在集成开发环境(如Visual Studio 2012及以上版本)中熟悉控制台应用程序的建立与调试。核心涉及命名空间:System.Reflection。
第1步:构建测试模型并定义类结构
为了演示反射的威力,我们需要一个包含各种访问修饰符的成员类。创建一个名为 RefClass 的类,其中包含不同可见度的字段和属性:
public class RefClass
{
private int _test3;
private int _test1 { get; set; }
protected int Test2 { get; set; }
public int Test3 { get; set; }
}
第2步:窥视类的全部公共成员
反射的第一步是理解目标类的结构。我们首先通过 typeof 获取类型,并使用 GetMembers() 进行初步扫描:
Type t = typeof(RefClass);
MemberInfo[] minfos = t.GetMembers();
foreach (MemberInfo minfo in minfos)
{
Console.WriteLine(minfo.Name);
}
观察结果:
如上输出所示,默认情况下 GetMembers() 仅返回公开(Public)的成员。由于所有类最终都继承自 Object,因此也会看到 GetType、ToString 等基类方法。值得注意的是,私有(Private)和保护(Protected)成员在此模式下被过滤掉了。
第3步:深入探查——筛选特定访问级别的成员
如果我们想要查看包括私有、受保护成员在内的完整内容,需要借助 BindingFlags 枚举参数:
MemberInfo[] minfos = t.GetMembers(
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.Public
);
说明:
NonPublic: 包含非公共成员。Instance: 包含实例成员(不包含静态成员)。Public: 包含公共成员。
输出效果:
进阶操作:排除父类干扰
如果在上述基础上只想关注当前类(而非其父类 Object)定义的成员,只需追加 DeclaredOnly:
MemberInfo[] minfos = t.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
此时结果变为:
第4步:识别成员的类型(字段 vs 方法 vs 属性)
单纯列出名字不够直观,我们可以利用 MemberType 进行区分。同时,如果希望查看到之前在步骤3中遗漏的静态方法,需加入 BindingFlags.Static:
// 假设 RefClass 中新增了 Show2() 和 Show3(string s) 方法...
MemberInfo[] minfos = t.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static );
foreach (MemberInfo minfo in minfos)
{
// 此处可根据 MemberTypes.Field / .Method / .Property 进行分类输出
Console.WriteLine($"名称: {minfo.Name}; 类型: {minfo.MemberType}");
}
执行对比:
第5步:深入字段操作——获取和修改私有变量值
这是反射最核心的应用之一。通常情况下,我们无法通过外部直接读写 _test1 等私有字段。但在反射面前,没有不可见的秘密:
- 获取: 使用
GetFields()拿到字段句柄,通过GetValue(instance)读取内存中的值。RefClass rc = new RefClass(); rc.Test3 = 3; // 设置一个公开属性来触发后台逻辑或者确保初始化 FieldInfo f = t.GetField("_test1", BindingFlags.NonPublic | BindingFlags.Instance); object val = f.GetValue(rc); - 修改: 通过
SetValue(instance, value)强行改变状态。f.SetValue(rc, 100);
过程图解:
可以看到即便字段标记为 Private,我们依然拿到了当前的内存赋值:
修改后:
第6步:深入属性操作——绕过 Get/Set 方法调用
属性(Property)底层实际上是成对的 getter 和 setter 方法。操作起来比字段复杂一些:
PropertyInfo[] props = t.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach (var prop in props)
{
// 获取 Setter 方法
MethodInfo setMethod = prop.GetSetMethod(true); // true 表示包括非公开的 setter
if(setMethod != null)
{
// 调用 Setter:第一个参数是对象实例,第二个是参数数组(这里设值为 123)
setMethod.Invoke(rc, new object[] { 123 });
}
// 获取 Getter 方法验证结果
MethodInfo getMethod = prop.GetGetMethod(true);
object result = getMethod.Invoke(rc, null);
Console.WriteLine($"属性 {prop.Name} 的新值为: {result}");
}
底层细节分析:
我们可以通过反射进一步分析这些生成方法的IL代码特征,例如查看GetMethodBody().GetILAsByteArray().Length 等元数据信息:
第7步:深入方法操作——动态执行带参方法
如果需要动态调用类中任意标记的方法:
- 使用
GetMethods()加上必要的BindingFlags定位目标方法。 - 检查参数匹配性(通过
GetParameters())以防止TargetInvocationException。 - 使用
Invoke(instance, parameters)执行。
MethodInfo m = t.GetMethod("Show3", BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
// 假设该方法接收 string 参数
string ret = (string)m.Invoke(null, new object[] { "Hello C# Reflection" });
在执行过程中,必须严格保证传入的参数数组顺序和类型与方法签名一致:
以上就是关于C#反射机制的详细实战教程。通过这个机制开发者可以灵活实现插件化框架设计、ORM映射转换等高级功能,更多关于C#高阶技术的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









