Ответ 1
Посмотрите на скомпилированные метаданные сборки, чтобы убедиться, что эти два свойства имеют идентичную структуру, кроме имени:
Я использую ILDASM вместо обычных инструментов декомпилятора, чтобы убедиться, что ничто не скрыто или не отображается более дружелюбно. Эти два свойства идентичны друг от друга.
Один из двух возвращаемых свойств Prop1
- от Class1
, а один из них - от Class2
.
Это кажется ошибкой. Кажется, что ошибка в том, что элементы базового класса неправильно добавлены к результатам. Если DeclaredOnly
не указано то также должны быть возвращены все унаследованные свойства.
Я использую 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, если вы запрашиваете частных членов... Я не знаю, почему. Кажется преднамеренным, однако.
Утомляет следовать этому свернутому коду. Это лучше и прокомментировано. Я подозреваю, что они удаляют частные свойства, потому что вы можете повысить привилегии, наследуя от какого-то класса, чтобы получить все частные члены.
И вот ответ:
// 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 может помочь.