在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生成两个方法:
- 实际重写方法:返回
DerivedProperty,是你代码中定义的逻辑实现。 - 桥接方法:返回
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
相关产品推荐
相关产品推荐

