如何检测协变返回类型属性存在且避免AmbiguousMatchException?
问题
我用Type.GetProperty方法验证要触发PropertyChanged的属性是否存在于类中,平时运行正常,但遇到用协变返回类型重写的属性时,会抛出AmbiguousMatchException。虽然能捕获异常,但调试时会被干扰,想找个更精准的方法直接检测属性存在性,彻底避免这个异常。
基础视图模型简化版
public abstract class Observable : INotifyPropertyChanged { protected void RaisePropertyChanged(string propertyName) { try { if (null == type.GetProperty(propertyName)) Debug.Fail($"{propertyName} is not a public property of {type.FullName}"); else PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } catch(AmbiguousMatchException) { } } }
协变返回类型的类示例
当在基类中检测"Geometry"属性时会触发异常:
public abstract class Shape : Observable { public abstract Geometry Geometry { get; } } public class Circle : Shape { public EllipseGeometry Geometry { get; } } public class Line : Shape { public LineGeometry Geometry { get; } }
解决方案
可以通过以下几种方法精准检测属性,避免AmbiguousMatchException:
方法1:遍历属性集合筛选匹配项
Type.GetProperty默认逻辑会同时匹配基类和派生类的协变属性导致歧义,改用遍历所有公共实例属性并筛选名称匹配项的方式,能避免异常:
protected void RaisePropertyChanged(string propertyName) { var hasProperty = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) .Any(p => p.Name == propertyName); if (!hasProperty) Debug.Fail($"{propertyName} is not a public property of {GetType().FullName}"); else PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
方法2:使用GetRuntimeProperty(.NET Core/.NET 5+)
在.NET Core及后续版本中,Type.GetRuntimeProperty会正确处理协变返回类型的重写属性,直接返回派生类中重写后的属性,不会抛出歧义异常:
protected void RaisePropertyChanged(string propertyName) { var property = GetType().GetRuntimeProperty(propertyName); if (property == null) Debug.Fail($"{propertyName} is not a public property of {GetType().FullName}"); else PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
方法3:递归遍历继承链检查
如果需要严格确认属性在继承链中的有效性,可以递归遍历类型的基类,逐个检查声明的属性:
private bool HasProperty(Type type, string propertyName) { if (type == null) return false; // 检查当前类型声明的属性 var property = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); if (property != null) return true; // 递归检查基类 return HasProperty(type.BaseType, propertyName); } protected void RaisePropertyChanged(string propertyName) { if (!HasProperty(GetType(), propertyName)) Debug.Fail($"{propertyName} is not a public property of {GetType().FullName}"); else PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

