方法也是如此:
我得到了两个PropertyInfo实例或方法,这些实例是通过GetProperty()
或GetMember()
等从(或者来自MemberExpression)的类中提取出来的。
我想确定它们是否实际上指的是相同的属性或相同的方法
(propertyOne == propertyTwo)
或
(methodOne == methodTwo)
显然,这实际上不起作用,您可能正在查看相同的属性,但它可能是从类层次结构的不同级别提取的(在这种情况下通常为propertyOne != propertyTwo
)
当然,我可以查看DeclaringType,并重新请求该属性,但是当你开始考虑
时,这开始变得有点混乱在一天结束时,我只想在两个属性或两个方法之间进行智能相等检查,我80%确定上述要点并未涵盖所有边缘情况,虽然我可以坐下来,写一堆测试并开始玩,我很清楚我对这些概念实际实现的低级知识不是优秀的,而我是希望这是一个已经回答过的话题,我只是在搜索时很糟糕。
最好的答案会给我一些实现上述目标的方法,解释已经处理的边缘案例及其原因: - )
澄清:
从字面上看,我想确保它们是相同的属性,这里有一些例子
public interface IFoo
{
string Bar { get; set; }
}
public class Foo : IFoo
{
string Bar { get; set; }
}
typeof(IFoo).GetProperty("Bar")
和
typeof(Foo).GetProperty("Bar")
将返回两个不相等的属性信息:
public class BaseClass
{
public string SomeProperty { get; set ; }
}
public class DerivedClass : BaseClass { }
typeof(BaseClass).GetMethod("SomeProperty")
和
typeof(DerivedClass).GetProperty("SomeProperty")
我实际上无法记住这两个现在是否会返回相同的对象,但在我的世界中它们是平等的。
类似地:
public class BaseClass
{
public virtual SomeMethod() { }
}
public class DerivedClass
{
public override SomeMethod() { }
}
typeof(BaseClass).GetMethod("SomeMethod")
和
typeof(DerivedClass).GetProperty("SomeMethod")
同样,这些不匹配 - 但我希望它们(我知道它们不是特别相同,但在我的域中它们是因为它们引用相同的原始属性)
我可以在结构上做到这一点,但这将是'错误'。
进一步说明:
您如何申请隐藏其他财产的财产?似乎我之前的一个假设是无效的,默认情况下GetProperty("name")
的默认实现将默认引用当前级别。
BindingFlags.DeclaringType
似乎最终会返回null!
答案 0 :(得分:3)
看一下PropertyInfo
/ IFoo
示例中的Foo
个对象,我们可以得出以下结论:
Type.GetInterfaces
并从那里开始工作。不要忘记接口可以实现其他接口,因此必须是递归的。所以让我们对它有所了解。首先,要涵盖继承的属性:
PropertyInfo GetRootProperty(PropertyInfo pi)
{
var type = pi.DeclaringType;
while (true) {
type = type.BaseType;
if (type == null) {
return pi;
}
var flags = BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.Public | BindingFlags.Static;
var inheritedProperty = type.GetProperty(pi.Name, flags);
if (inheritedProperty == null) {
return pi;
}
pi = inheritedProperty;
}
}
现在,要覆盖在接口中声明的属性(使用DFS搜索):
PropertyInfo GetImplementedProperty(PropertyInfo pi)
{
var type = pi.DeclaringType;
var interfaces = type.GetInterfaces();
if (interfaces.Length == 0) {
return pi;
}
var flags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public;
var query = from iface in interfaces
let implementedProperty = iface.GetProperty(pi.Name, flags)
where implementedProperty != pi
select implementedProperty;
return query.DefaultIfEmpty(pi).First();
}
将这些联系在一起:
PropertyInfo GetSourceProperty(PropertyInfo pi)
{
var inherited = this.GetRootProperty(pi);
if (inherited != pi) {
return inherited;
}
var implemented = this.GetImplementedProperty(pi);
if (implemented != pi) {
return implemented;
}
return pi;
}
这应该有效。它没有考虑具有相同名称但索引参数的类型和/或数量不同的索引属性,因此留下作为读者的谚语练习。
免责声明:我甚至没有编译它(现在没时间运行测试)。它旨在作为“答案”的起点,因为到目前为止还没有。
答案 1 :(得分:2)
我不确定你需要什么,但我想在这种情况下你的相等定义是“如果调用两个方法信息调用相同的方法”?如果是这样,那么你真的需要在比较中指定一个类型,例如考虑这个:
public interface IFoo
{
void AMethod();
}
public interface IBar
{
void AMethod();
}
public class FooBar : IFoo, IBar
{
void AMethod();
}
显然typeof(IFoo).GetMethod(“AMethod”)和typeof(IBar).GetMethod(“AMethod”)不相等,它们甚至不相关,但它们确实调用相同的FooBar实例上的方法。所以你可能想要的是一个带有三个参数的比较方法:
bool WillInvokeSameMethodOnType(MethodInfo method1, MethodInfo method2, Type type)
查看FakeItEasy中的MethodInfoManager类,它可能是你想要的:http://code.google.com/p/fakeiteasy/source/browse/Source/FakeItEasy/Core/MethodInfoManager.cs?r=8888fefbc508fb02d5435a3e33774500bec498b3
答案 2 :(得分:2)
所以,这是一个艰难的cookie,在我讨论枯燥的细节之前,我会说这个,我最终选择进行结构比较,因为 唯一的地方会掉下来,是一个成员隐藏另一个成员在C#中使用'new'关键字 - 决定这个系统中的一个小问题,与其他多种伤害相比,这个问题的正确解决方案最终会导致它出错了( 出错了,相信我)。
我接受了上面的答案,因为它接近于解决问题 - 它唯一的问题是仍然结构性质(我可以通过类型/名称/参数实现这一点)检查)
虽然存在一些差异,主要是有必要查看界面地图而不是调用GetProperty - 这是一个非常重要的细节 - 这是我修改后的方法,在某些情况下会失败。
private PropertyInfo GetImplementedProperty(PropertyInfo pi)
{
var type = pi.DeclaringType;
var interfaces = type.GetInterfaces();
for(int interfaceIndex = 0; interfaceIndex < interfaces.Length; interfaceIndex++)
{
var iface = interfaces[interfaceIndex];
var interfaceMethods = type.GetInterfaceMap(iface).TargetMethods;
MethodInfo matchingMethod = null;
for (int x = 0; x < interfaceMethods.Length; x++)
{
if (pi.GetGetMethod().LooseCompare(interfaceMethods[x]) || pi.GetSetMethod().LooseCompare(interfaceMethods[x]))
{
matchingMethod = type.GetInterfaceMap(iface).InterfaceMethods[x];
break;
}
}
if (matchingMethod == null) continue;
var interfacePi = from i in interfaces
from property in i.GetProperties()
where property.GetGetMethod().LooseCompare(matchingMethod) || property.GetSetMethod().LooseCompare(matchingMethod)
select property;
return interfacePi.First();
}
return pi;
}
我最后放弃检查一名成员是否隐藏了另一名成员,然后进行了以下黑客攻击:
private PropertyInfo GetRootProperty(PropertyInfo pi)
{
if ((pi.GetGetMethod().Attributes & MethodAttributes.Virtual) != MethodAttributes.Virtual) { return pi; }
var type = pi.DeclaringType;
while (true)
{
type = type.BaseType;
if (type == null)
{
return pi;
}
var flags = BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.Public | BindingFlags.Static;
var inheritedProperty = type.GetProperty(pi.Name, flags);
if (inheritedProperty == null)
{
return pi;
}
pi = inheritedProperty;
}
}
我在这里假设一个使用'new'关键字的属性/方法也不会使用virtual关键字,因为看起来'new'无论如何都是一个边缘情况,这是不太可能的。
这是我在决定让我的测试通过之前所得到的,我很满意。 (在我决定选择结构检查之前......)我希望这对任何在将来偶然发现的人都有用。
答案 3 :(得分:1)
检查要比较的两个MemberInfo
的声明类型似乎更简单。在基本/子类关系的情况下,如果声明类型相同,它们应表示相同的声明。在接口的情况下,如果声明接口类型在另一个接口的列表中,它们应该是相同的:
Type type1 = methodInfo1.DeclaringType;
Type type2 = methodInfo2.DeclaringType;
bool same = type1 == type2 ||
type1.IsInterface && type2.GetInterfaces.Contains(type1) ||
type2.IsInterface && type1.GetInterfaces.Contains(type2);
关于接口需要注意的一点是'接口映射' - Type.GetInterfaceMap,这意味着接口中声明的方法在实现类中可能没有相同的名称,而当前的方法似乎没有帐户。