我一直在使用
public static bool IsSameAsProperty(PropertyInfo first, PropertyInfo second) =>
first.DeclaringType == second.DeclaringType && first.Name == second.Name;
确定反射的属性信息是否与某些属性相匹配,我从基类中抓取了。
当我尝试在接口中定义的属性时,这种方法已经开始崩溃。
例如,想象以下多个接口继承方案:
interface IAnimal : { bool IsHungry { get; } }
interface IDog : IAnimal { }
abstract class Animal : IAnimal { public bool IsHungry { get; set; } }
class Dog : Animal, IDog { }
如果我要创建属性表达式,则所有以下所有内容都是有效的:
Expression<Func<object, bool>> propertyExpression;
propertyExpression = (IAnimal animal) => animal.IsHungry
propertyExpression = (Animal animal) => animal.IsHungry
propertyExpression = (IDog dog) => dog.IsHungry
propertyExpression = (Dog dog) => dog.IsHungry
由于这些类型中的每一种都定义或继承了属性IsHungry
,因此所有这些表达式都是有效的。甚至有人可能会说他们都指的是同一属性(尽管我可以欣赏界面和实例声明之间的细微差异)。
我的问题是,我需要某种方法来检测所有这些属性"都来自"共享接口IAnimal
并兼容。不幸的是,我的测试返回false
是因为:
-
IDog.IsHungry
具有DeclaringType == typeof(IAnimal)
,而 -
Dog.IsHungry
有DeclaringType == typeof(Animal)
我想不出一种简单的方法来比较界面和具体类型的属性表达式,而不要求助于简单的Name
比较(这很容易发生误报) - 但我无法想到任何没有的东西涉及列举两种类型所继承的所有接口,并寻找两套属性名称的任何东西。
Q:我们可以创建一个函数,该函数在比较上述4个属性表达式产生的任何属性时返回true。(例如,检测它们都表示/代表/实现相同的基本接口属性吗?)
,如果使用new
关键字用于隐藏继承的属性,这可能会导致误报,但是:
public static bool IsSameAsProperty(PropertyInfo first, PropertyInfo second) =>
first == second || // If default equals implementation returns true, no doubt
first.Name == second.Name && (
first.DeclaringType == second.DeclaringType ||
first.DeclaringType.IsAssignableFrom(second.DeclaringType) ||
second.DeclaringType.IsAssignableFrom(first.DeclaringType));
我想我可能会更具体一点,并检查 first.DeclaringType.IsInterface
和vice-vices-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice》中仍然可以明确地实现并用具有相同名称的新属性隐藏其属性,因此额外的检查不会使其任何"更安全"。
不确定我是否可以告诉一个PropertyInfo
实例是否代表另一个的接口实现。
我想出的解决方案将getter/setter MethodInfo
与InterfaceMapping
进行了比较。它通过了我能想到的所有测试,但我并不是100%确信没有奇怪的拐角案件。
public static bool IsSameAsProperty(PropertyInfo first, PropertyInfo second)
{
if (first.DeclaringType == second.DeclaringType && first.Name == second.Name)
return true;
bool firstIsSecond = second.DeclaringType.IsAssignableFrom(first.DeclaringType);
bool secondIsFirst = first.DeclaringType.IsAssignableFrom(second.DeclaringType);
if (firstIsSecond || secondIsFirst)
{
PropertyInfo baseProp = firstIsSecond ? second : first;
PropertyInfo derivedProp = firstIsSecond ? first : second;
MethodInfo baseMethod, implMethod;
if (baseProp.GetMethod != null && derivedProp.GetMethod != null)
{
baseMethod = baseProp.GetMethod;
implMethod = derivedProp.GetMethod;
}
else if (baseProp.SetMethod != null && derivedProp.SetMethod != null)
{
baseMethod = baseProp.SetMethod;
implMethod = derivedProp.SetMethod;
}
else
{
return false;
}
// Is it somehow possible to create a situation where both get and set exist
// and the set method to be an implementation while the get method is not?
if (baseMethod.DeclaringType.IsInterface)
return IsInterfaceImplementation(implMethod, baseMethod);
else
return IsOverride(implMethod, baseMethod);
}
return false;
}
private static bool IsInterfaceImplementation(MethodInfo implMethod, MethodInfo interfaceMethod)
{
InterfaceMapping interfaceMap = implMethod.DeclaringType.GetInterfaceMap(interfaceMethod.DeclaringType);
int index = Array.IndexOf(interfaceMap.InterfaceMethods, interfaceMethod);
// I don't think this can ever be the case?
if (index == -1)
return false;
MethodInfo targetMethod = interfaceMap.TargetMethods[index];
return implMethod == targetMethod || IsOverride(implMethod, targetMethod);
}
private static bool IsOverride(MethodInfo implMethod, MethodInfo baseMethod)
{
return implMethod.GetBaseDefinition() == baseMethod.GetBaseDefinition();
}