当我宣布以下简单的类:
class Class1<T> { protected virtual T Prop1 { get; set; } protected virtual string Prop2 { get; set; } } class Class2 : Class1<string> { protected override string Prop1 { get; set; } protected override string Prop2 { get; set; } }
现在我使用Reflection来获取Class2的属性,如下所示:
var hProperties = typeof(Class2).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);
那么Prop2将被列出一次,而Prop1将被列出两次!这个行为对我来说很奇怪. Prop1和Prop2不应该被视为相同吗?
在Hproperties中只能使用Prop1一次?我不想使用BindingFlags.DeclaredOnly,因为我也想获得不被覆盖的其他受保护的Class1属性.
解决方法
我们来看看编译程序集的元数据,以确保这两个属性与名称不同:
我正在使用ILDASM而不是通常的反编译器工具,以确保没有隐藏或以更友好的方式显示.这两个属性与名称相同.
返回的两个Prop1属性之一来自Class1,其中一个来自Class2.
这似乎是一个错误.错误似乎是基类成员未正确添加到结果中.当DeclaredOnly未指定all inherited properties should be returned as well时.
我使用DotPeek和Reflector VS扩展,允许调试反编译的BCL代码来调试反射代码.在这个问题中看到的行为是以这种方式触发的:
private void PopulateProperties(RuntimeType.RuntimeTypeCache.Filter filter,RuntimeType declaringType,Dictionary<string,List<RuntimePropertyInfo>> csPropertyInfos,bool[] usedSlots,ref RuntimeType.ListBuilder<RuntimePropertyInfo> list) { int token = RuntimeTypeHandle.GetToken(declaringType); if (MetadataToken.IsNullToken(token)) return; MetadataEnumResult result; RuntimeTypeHandle.GetMetadataImport(declaringType).EnumProperties(token,out result); RuntimeModule module = RuntimeTypeHandle.GetModule(declaringType); int numVirtuals = RuntimeTypeHandle.GetNumVirtuals(declaringType); for (int index1 = 0; index1 < result.Length; ++index1) { int num = result[index1]; if (filter.RequiresStringComparison()) { if (ModuleHandle.ContainsPropertyMatchingHash(module,num,filter.GetHashToMatch())) { Utf8String name = declaringType.GetRuntimeModule().MetadataImport.GetName(num); if (!filter.Match(name)) continue; } else continue; } bool isPrivate; RuntimePropertyInfo runtimePropertyInfo = new RuntimePropertyInfo(num,declaringType,this.m_runtimeTypeCache,out isPrivate); if (usedSlots != null) { if (!(declaringType != this.ReflectedType) || !isPrivate) { MethodInfo methodInfo = runtimePropertyInfo.GetGetMethod(); if (methodInfo == (MethodInfo) null) methodInfo = runtimePropertyInfo.GetSetMethod(); if (methodInfo != (MethodInfo) null) { int slot = RuntimeMethodHandle.GetSlot((IRuntimeMethodInfo) methodInfo); if (slot < numVirtuals) { if (!usedSlots[slot]) usedSlots[slot] = true; else continue; } } if (csPropertyInfos != null) { string name = runtimePropertyInfo.Name; List<RuntimePropertyInfo> list1 = csPropertyInfos.GetValueOrDefault(name); if (list1 == null) { list1 = new List<RuntimePropertyInfo>(1); csPropertyInfos[name] = list1; } for (int index2 = 0; index2 < list1.Count; ++index2) { if (runtimePropertyInfo.EqualsSig(list1[index2])) { list1 = (List<RuntimePropertyInfo>) null; break; } } if (list1 != null) list1.Add(runtimePropertyInfo); else continue; } else { bool flag = false; for (int index2 = 0; index2 < list.Count; ++index2) { if (runtimePropertyInfo.EqualsSig(list[index2])) { flag = true; break; } } if (flag) continue; } } else continue; } list.Add(runtimePropertyInfo); } }
为什么公共物业的行为消失?
if (!(declaringType != this.ReflectedType) || !isPrivate)
有一个检查.
Class1< string> .Prop2在此被过滤掉:
bool flag = false; for (int index2 = 0; index2 < list.Count; ++index2) { if (runtimePropertyInfo.EqualsSig(list[index2])) { flag = true; break; } } if (flag) continue;
因为EqualsSig返回true.看来,如果要求私人会员,属性将按名称和sig进行重复数据删除…我不知道为什么.似乎有意思.
遵循这个复杂的代码是很累的. This is better and commented.我怀疑他们正在删除私有属性,因为您可以通过继承某些类来提升特权,以获取所有私有成员.
这里是答案:
// For backward compatibility,even if the vtable slots don't match,we will still treat // a property as duplicate if the names and signatures match.
所以他们为后向兼容性添加了一个黑客.
您必须添加自己的处理才能获得所需的行为.也许,Fastreflect可以帮助.