java优先用getdeclaredmethods()/getdeclaredfields()获取本类全部成员(含private),需setaccessible(true);python用inspect.getmembers()配合谓词和__objclass__判断定义位置;c#需显式指定bindingflags;三者均受模块化/权限限制,须预检。

Java 中用 Class.getDeclaredMethods() 和 Class.getDeclaredFields() 获取全部成员
反射获取方法和属性,核心不是“找得到”,而是“找得全且安全”。getDeclaredMethods() 返回本类声明的所有方法(含 private),但不包括继承的;如果需要父类方法,得手动遍历 getSuperclass() 链。同理,getDeclaredFields() 只返回本类字段,getFields() 虽然返回 public 字段(含父类),但会漏掉 private 和 protected 字段——多数场景下你要的是“本类定义的完整结构”,所以优先选 getDeclaredXXX 系列。
注意访问控制:默认无法读取 private 成员,必须先调用 setAccessible(true),否则在 JDK 12+ 会抛 java.lang.InaccessibleObjectException(尤其模块化环境下)。
示例片段:
Class> clazz = MyClass.class;
for (Method m : clazz.getDeclaredMethods()) {
m.setAccessible(true); // 必须加,否则 private 方法调用会失败
System.out.println(m.getName());
}
for (Field f : clazz.getDeclaredFields()) {
f.setAccessible(true);
System.out.println(f.getName() + " : " + f.getType());
}
Python 的 dir() 不够用,该用 inspect.getmembers() 过滤真实属性
dir() 返回的是名字列表,混着很多魔术方法、继承来的属性、甚至动态生成的别名,不能直接当“本类定义的属性”用。真正可控的方式是 inspect.getmembers(obj, predicate=...),配合 inspect.isfunction、inspect.isdatadescriptor 等谓词筛选。
关键点在于区分“定义位置”:__dict__ 只包含实例属性,类属性得查 cls.__dict__;而方法是否属于当前类,要用 getattr(member, '__objclass__', None) is cls 判断(避免把父类方法误认为本类)。
常用组合:
- 获取本类定义的方法:
[name for name, obj in inspect.getmembers(cls, inspect.isfunction) if getattr(obj, '__objclass__', None) is cls] - 获取本类定义的数据属性(非方法、非描述符):
[name for name, obj in cls.__dict__.items() if not name.startswith('_') and not inspect.isfunction(obj) and not isinstance(obj, (property, classmethod, staticmethod))]
C# 的 Type.GetMethods() 默认不返回 private,得传 BindingFlags
C# 反射比 Java 更显式:不指定 BindingFlags,GetMethods() 默认只返回 public 实例方法。要拿到 private、static、继承的,必须拼接标志位,比如 BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static。
常见错误是只写 BindingFlags.NonPublic 却漏了 BindingFlags.Instance,结果 private 实例方法还是拿不到。另外,GetProperties() 同理,且要注意 PropertyInfo.GetAccessors(includePrivate: true) 才能检查 private getter/setter 是否存在。
性能提示:频繁调用反射获取方法列表(如每帧一次)会显著拖慢性能,建议缓存结果,或改用 System.Reflection.Emit 或源生成(Source Generators)预生成元数据。
所有语言都绕不开的权限与模块化限制
JDK 9+ 模块系统默认禁止反射访问非开放包(如 java.base 下的 sun.* 类),即使用了 setAccessible(true) 也会被拒;.NET Core 3.0+ 对某些内部类型(如 System.Private.CoreLib)也做了类似限制;Python 的 __slots__ 类还会让 __dict__ 为空,导致字段枚举失效。
这意味着:没有“通用无条件反射”的方案。你得根据目标环境提前判断——是否运行在模块化 JVM 上?是否启用了 .NET 的 TrimMode=link?目标类是否用了 __slots__ 或冻结(typing.Final)?这些都会让看似正常的反射调用在特定部署环境下静默失败或抛异常。
最稳妥的做法,是在初始化阶段就尝试反射并捕获异常,记录实际可用的成员范围,而不是等到业务逻辑中才暴露问题。










