自定义MemberInfo子类设MemberTypes.Custom后Distinct()去重失效问题
1. 问题原因
Linq的Distinct()默认依赖EqualityComparer<T>.Default做对象相等性判断,对于继承自MemberInfo的类型,这个默认比较器会结合MemberType等核心属性完成校验。
你把VariableInfo的MemberType重写为MemberTypes.Custom后,即便两个实例封装的是同一个FieldInfo/PropertyInfo(哈希值看似一致),默认相等判断逻辑会因MemberType不是原始的MemberTypes.Field或MemberTypes.Property,判定两个实例不相等,最终导致Distinct()无法去重。而恢复原始MemberType后,默认比较器能正确识别到两个实例指向同一成员,去重功能自然恢复正常。
另外,你之前修改MemberType是为了修复Attribute.IsDefined失效,本质是系统原生的Attribute.IsDefined逻辑会校验MemberType是否为预期的字段/属性类型,当MemberType设为Custom时,意外触发了兼容逻辑让属性检查转发到内部封装的成员上,但这同时破坏了MemberInfo默认的相等性判断规则。
2. 解决方案(不破坏Attribute.IsDefined功能)
方案一:重写VariableInfo的相等性判断逻辑
直接在VariableInfo类中重写Equals和GetHashCode,让相等性判断基于内部封装的FieldInfo/PropertyInfo,而非依赖MemberInfo的默认逻辑:
public class VariableInfo : MemberInfo { private readonly MemberInfo _innerMember; // 构造函数及其他原有代码... public override bool Equals(object obj) { if (obj is not VariableInfo other) return false; return _innerMember.Equals(other._innerMember); } public override int GetHashCode() { return _innerMember.GetHashCode(); } // 保留之前为修复Attribute.IsDefined重写的MemberType }
这样Distinct()会基于内部成员的相等性去重,同时不影响Attribute.IsDefined的功能。
方案二:修复Attribute.IsDefined的标准姿势(推荐)
修改MemberType属于临时 workaround,正确的做法是重写Attribute.IsDefined相关方法,直接委托给内部封装的成员,这样可以把MemberType恢复为原始值,让Distinct()的默认逻辑正常工作:
public class VariableInfo : MemberInfo { private readonly MemberInfo _innerMember; // 构造函数及其他原有代码... // 恢复MemberType为内部成员的类型 public override MemberTypes MemberType => _innerMember.MemberType; // 重写属性检查方法,委托给内部成员 public override bool IsDefined(Type attributeType, bool inherit) { return _innerMember.IsDefined(attributeType, inherit); } // 按需重写其他属性相关方法 public override object[] GetCustomAttributes(Type attributeType, bool inherit) { return _innerMember.GetCustomAttributes(attributeType, inherit); } public override object[] GetCustomAttributes(bool inherit) { return _innerMember.GetCustomAttributes(inherit); } }
这种方式既从根源解决了Attribute.IsDefined的问题,又保留了MemberInfo默认的相等性逻辑,Distinct()可以正常去重,更符合.NET设计规范。
方案三:使用自定义相等比较器
如果不想修改VariableInfo类,可以实现IEqualityComparer<VariableInfo>,在调用Distinct()时传入该比较器:
public class VariableInfoEqualityComparer : IEqualityComparer<VariableInfo> { public bool Equals(VariableInfo x, VariableInfo y) { if (x == null || y == null) return x == y; return x.InnerMember.Equals(y.InnerMember); // 假设InnerMember是暴露的内部成员属性 } public int GetHashCode(VariableInfo obj) { return obj.InnerMember.GetHashCode(); } } // 调用Distinct时使用 var distinctVariables = GetAllVariables().Distinct(new VariableInfoEqualityComparer()).ToList();
这种方式无需修改VariableInfo原有实现,适合快速适配场景。
内容的提问来源于stack exchange,提问作者A person

