为什么GetProperties列出受保护的属性(在通用基类中声明)两次?

spe*_*try 10 .net c# reflection

当我声明以下简单类时:

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; }
}
Run Code Online (Sandbox Code Playgroud)

现在我使用Reflection来获取Class2的属性,如下所示:

var hProperties = typeof(Class2).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);
Run Code Online (Sandbox Code Playgroud)

然后Prop2将列出一次,而Prop1将列出两次!这种行为对我来说似乎很奇怪.Prop1和Prop2不应该被视为相同吗?

如何在hProperties中只使用Prop1一次?我不想使用BindingFlags.DeclaredOnly,因为我还想获得未被覆盖的Class1的其他受保护属性.

usr*_*usr 6

让我们看一下已编译程序集的元数据,以确保这两个属性具有与名称不同的结构:

在此输入图像描述

我正在使用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);
      }
    }
Run Code Online (Sandbox Code Playgroud)

为什么公共财产的行为会消失?

      if (!(declaringType != this.ReflectedType) || !isPrivate)
Run Code Online (Sandbox Code Playgroud)

有一个检查.

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;
Run Code Online (Sandbox Code Playgroud)

因为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.
Run Code Online (Sandbox Code Playgroud)

所以他们为了向后兼容性添加了一个hack.

您必须添加自己的处理才能获得所需的行为.也许,Fastreflect可以提供帮助.