GetProperties()返回空数组仅表示无public实例属性,不等于类型无属性;GetProperty("Name")返回null才代表属性不存在,需配合BindingFlags使用并校验null和dynamic等场景。

GetProperties() 返回空数组不等于没有属性
调用 Type.GetProperties() 得到空数组,不代表类型没定义任何属性——它只返回 public 实例属性。private、static、protected、getter-only(C# 6+)、编译器生成的 backing field(如 auto-property)对应的实际字段都不在其中。更关键的是,GetProperties() 不会抛异常,但返回 null 是不可能的;真正容易出错的是后续链式调用 GetProperty("Name")?.GetValue(obj) 中的 ?. 操作符掩盖了属性不存在的事实,导致静默返回 null,而你本意可能是报错或 fallback。
GetProperty("Name") 返回 null 就代表属性不存在
Type.GetProperty(string name) 是最直接的判断方式,但它区分大小写、不支持继承链自动查找(除非传 BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy),且对索引器、泛型参数名等特殊场景无效。常见误用是没传 binding flags:
var prop = obj.GetType().GetProperty("Id"); // ❌ 默认只查 public instance,父类 public 属性拿不到正确做法是显式指定常用组合:
-
BindingFlags.Public | BindingFlags.Instance:查当前类型的 public 实例属性(不含父类) -
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly:排除继承来的属性 -
BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy:包含父类 public 属性(但不包括 private 父类属性)
注意:FlattenHierarchy 对 protected 属性无效,它只影响 public 成员的继承可见性。
需要兼容 null 或 dynamic 对象?先做 Type 检查
对 null 调用 GetType() 会抛 NullReferenceException;对 dynamic 或 COM 对象,GetProperty 可能返回 null 或抛 RuntimeBinderException。所以安全判断必须前置校验:
- 对象为 null → 直接返回 false(null 没有属性)
- 对象是
dynamic→ 改用TryGetMember(需实现IDynamicMetaObjectProvider)或捕获异常 - 对象是匿名类型 →
GetProperty可用,但属性名受编译器生成规则约束(如<Name>j__TPar),不建议硬编码字符串匹配 - 对象是接口类型(如
IEnumerable<T>)→GetProperty查的是接口定义的属性,不是实现类的,别混淆
典型防空写法:
public static bool HasProperty(object obj, string propertyName) {<br> if (obj == null) return false;<br> var type = obj.GetType();<br> return type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance) != null;<br>}想支持表达式树或 nameof?避免字符串硬编码
用字符串查属性名容易拼错、重构时漏改。更健壮的方式是结合 Expression 和 nameof:
public static bool HasProperty<T>(T obj, Expression<Func<T, object>> propertyExpr) {<br> if (obj == null) return false;<br> var member = propertyExpr.Body as MemberExpression;<br> if (member == null) return false;<br> return member.Member is PropertyInfo;<br>}<br>// 使用:<br>HasProperty(myObj, x => x.Name); // ✅ 编译期检查,支持重命名但要注意:这种写法无法处理嵌套属性(x => x.Address.City),且反射开销略高;若只是简单判断,字符串 + GetProperty 更轻量。二者取舍看是否需要 IDE 重构支持和类型安全。
真正容易被忽略的是:属性存在 ≠ 可读取。比如只有 setter 的属性(string Name { set; }),GetProperty 能拿到,但 GetValue 会抛 InvalidOperationException。判断可读性得额外查 CanRead 属性。


















