You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何检测协变返回类型属性存在且避免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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 17:01:17