我一直在使用
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
比较(这容易导致假阳性)-但是我什么都想不起来无需枚举由这两种类型继承的所有接口,并查找具有两个集合中的属性名称的任何内容。
问:我们是否可以创建一个比较上述4个属性表达式产生的PropertyInfo时返回true的函数。(例如,检测它们是否都表示/实现相同的基本接口属性?)
答案 0 :(得分:0)
在使用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
是否正确,反之亦然,但是仍然可以显式实现该接口,并使用具有相同名称的新接口隐藏其属性,因此,检查不会使其变得“更安全”。
不确定是否可以确定一个PropertyInfo
实例是否代表另一个实例的接口实现。
答案 1 :(得分:0)
我想出的解决方案将获取器/设置器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();
}