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

在C# 9中如何通过反射判断协变属性重写对应的基类属性?

如何判断协变重写属性与基类抽象属性的重写关系?

这个问题的核心在于C#编译器对协变返回类型重写的特殊处理——它会生成一个隐藏的桥接方法,导致我们常规的GetBaseDefinition()判断失效。让我们一步步拆解原因和解决方案:

问题重现

首先看你提供的代码示例:

class BaseProperty { }
class DerivedProperty : BaseProperty { }
abstract class BaseWithAbstractProperty {
    public abstract BaseProperty Property { get; }
    public virtual BaseProperty OtherProperty { get; }
}
sealed class DerivedWithCovariantOverride : BaseWithAbstractProperty {
    public override DerivedProperty Property { get; }
    public override BaseProperty OtherProperty { get; }
}

对于普通的OtherProperty,用GetBaseDefinition()可以正常判断重写关系,但协变的Property却返回false——这是因为编译器为协变重写生成了桥接方法。

为什么常规方法失效?

CLR本身并不支持协变返回类型的方法重写,所以C#编译器会做一层适配:

  • 为协变属性的getter生成两个方法:
    1. 实际重写方法:返回DerivedProperty,是你代码中定义的逻辑实现。
    2. 桥接方法:返回BaseProperty,标记有IsBridge和PreserveBaseOverridesAttribute,内部调用实际重写方法,用来满足CLR的类型系统要求。
  • 当你用GetProperty().GetMethod获取协变属性的get方法时,拿到的是桥接方法,而它的GetBaseDefinition()指向的不是基类的抽象方法,导致判断失败。

解决方案

我们需要绕过桥接方法,找到实际的重写方法,再进行判断。这里提供几种可行的方式:

方法1:过滤桥接方法

直接从派生类中获取所有同名的get方法,过滤掉标记为IsBridge的方法:

var baseProperty = typeof(BaseWithAbstractProperty)
    .GetProperty("Property", BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);

// 获取实际的重写get方法(非桥接)
var actualDerivedGetMethod = typeof(DerivedWithCovariantOverride)
    .GetMethods(BindingFlags.Instance | BindingFlags.Public)
    .Where(m => m.Name == "get_Property" && !m.IsBridge)
    .Single();

// 现在判断就会返回true
Console.WriteLine(actualDerivedGetMethod.GetBaseDefinition() == baseProperty.GetMethod);

方法2:直接获取运行时方法

使用GetRuntimeMethod可以直接拿到实际的重写方法(它会跳过编译器生成的桥接方法):

var baseProperty = typeof(BaseWithAbstractProperty)
    .GetProperty("Property", BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);

var actualDerivedGetMethod = typeof(DerivedWithCovariantOverride)
    .GetRuntimeMethod("get_Property", Type.EmptyTypes);

Console.WriteLine(actualDerivedGetMethod.GetBaseDefinition() == baseProperty.GetMethod); // 返回true

方法3:封装通用判断方法

如果你需要频繁处理这类场景,可以封装一个通用方法,兼容普通重写和协变重写:

public static bool IsPropertyOverride(PropertyInfo derivedProperty, PropertyInfo baseProperty)
{
    if (derivedProperty == null || baseProperty == null)
        return false;

    // 确定派生类属性的实际get方法(跳过桥接方法)
    var derivedGetMethod = derivedProperty.GetMethod.IsBridge
        ? derivedProperty.DeclaringType.GetMethods(BindingFlags.Instance | BindingFlags.Public)
            .First(m => m.Name == derivedProperty.GetMethod.Name && !m.IsBridge)
        : derivedProperty.GetMethod;

    return derivedGetMethod.GetBaseDefinition() == baseProperty.GetMethod;
}

调用示例:

var baseProp = typeof(BaseWithAbstractProperty).GetProperty("Property", BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
var derivedProp = typeof(DerivedWithCovariantOverride).GetProperty("Property", BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);

Console.WriteLine(IsPropertyOverride(derivedProp, baseProp)); // 返回true

内容的提问来源于stack exchange,提问作者Dennis Doomen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 23:27:48