【问题标题】:Iterating over base type properties first when calling Reflection's GetMembers?调用反射的 GetMembers 时首先迭代基类型属性?
【发布时间】:2010-12-27 15:18:25
【问题描述】:

我正在使用反射来迭代给定类型的所有成员。这种交互必须支持继承,因为大多数类型都会根据需要进行扩展。

我刚刚发现,当迭代 GetMembers 时,类型出现的顺序并不是我所期望的——派生类的类型首先出现,按照它们当前的顺序,然后之后的基类,再次按当前顺序。

来源:

using System;

class Program 
{
        class SomeClass
        {
            public string First { get; set; }
            public int Second;
        }

        class AnotherClass : SomeClass
        {
            public int Third { get; set; }
            public string Fourth;
        } 

        public static void Main()
        {
            var obj = new AnotherClass { First = "asd", Second = 10};

            foreach (var member in obj.GetType().GetMembers())
            {
                Console.WriteLine(member.Name);
            }
        }
}

输出:

    get_Third
    set_Third
    get_First
    set_First
    Equals
    GetHashCode
    GetType
    ToString
    .ctor
    Third
    First
    Fourth
    Second

您可以检查运行here

我想反转这种情况,使用反射仅从基类中获取类型,然后从派生类中获取。有没有办法做到这一点?

同一行的另一个问题:搜索成员时,属性在前,字段在后。无论如何也要更改此行为,或者创建的元数据将始终按该顺序显示?

谢谢!

【问题讨论】:

    标签: c# reflection


    【解决方案1】:

    要访问基本类型,请使用BaseType 属性。

    要检查成员是否声明为同一类型,请使用DeclaringType 属性:

    public static bool DeclaredInType(Type typeToCheck, MemberInfo member)
    {
        return typeToCheck.Equals(member.DeclaringType);
    }    
    

    编辑:您可以使用 LINQ 按类型排序:

    public static MemberInfo[] SortMembers(IEnumerable<MemberInfo> members)
    {
        return members.OrderBy(m => m.GetType().Name)
                      .ToArray();
    }
    

    【讨论】:

    • 谢谢,看起来很简单。我会试试的。
    • 它工作正常,刚才我明白你所说的检查声明类型。完全有必要,否则我会一遍又一遍地得到所有类型。一个问题:无论如何要问反射不要给我继承的类型(例如通过绑定标志)?我已经尝试过了,但无法。
    • @Bruno Brant 这已经晚了将近两年,但您可以使用 BindingFlags.DeclaredOnly 进行过滤。
    【解决方案2】:

    这并不完全是对 OP 的回答(我对此有点晚了),但我只会描述我所做的事情,希望有人会觉得它有帮助。

    我有一个序列化为 XML 的自制程序。它由我创建的包含加快序列化所需的数据的 CopierField 对象列表驱动。这是该类的一个经过大量编辑的版本:

      private class CopierField
      {
         // Name of the field or property and a reference to the declaring Type
         public string MemberName;
         public Type DeclaringType;
    
         // Reference to the FieldInfo or PropertyInfo object for this field or property. One of 
         //  these will be null and the other non-null.
         public FieldInfo MemberInfoForField = null;
         public PropertyInfo MemberInfoForProperty = null;
    
         // Ordering of this field, as returned by Type.GetMembers(). This is only used while 
         //  building the List<> of these objects for XML serialization
         public int FieldOrder;
    
    
         /// <summary>
         /// Comparison method that can be used to sort a collection of CopierField objects so they 
         /// are in the order wanted for XML serialization. This ordering is dependent on the depth 
         /// of derivation, and when that is equal it maintains the original ordering.
         /// </summary>
         public static readonly Comparison<CopierField> CompareForXml =
                            delegate(CopierField a, CopierField b)
                            {
                               int aDepth = GetTypeDepth(a.DeclaringType);
                               int bDepth = GetTypeDepth(b.DeclaringType);
                               if (aDepth != bDepth)
                                  return aDepth.CompareTo(bDepth);
    
                               return a.FieldOrder.CompareTo(b.FieldOrder);
                            };
    
    
         /// <summary>
         /// Method to determine the depth of derivation for a type.
         /// </summary>
         private static int GetTypeDepth(Type aType)
         {
            int i = 0;
            while (aType.BaseType != null)
            {
               i++;
               aType = aType.BaseType;
            }
            return i;
         }
      }
    

    在第一次将对象类型序列化为 XML 之前,我创建了这些对象的列表,这些对象按我想要的顺序排序,其中输入是基于来自 Type 的数据的 CopierField 对象列表.GetMembers()。

      private static List<CopierField> CreateCopierFieldListForXml(List<CopierField> copierFields)
      {
         // Build the new list, and note the original ordering as created by Type.GetMembers()
         List<CopierField> copierFieldsForXml = new List<CopierField>(copierFields.Count);
         for (int listIndex = 0; listIndex < copierFields.Count; listIndex++)
         {
            CopierField copierField = copierFields[listIndex];
            copierField.FieldOrder = listIndex;
            copierFieldsForXml.Add(copierField);
         }
    
         // Sort the new list as wanted for XML serialization
         copierFieldsForXml.Sort(CopierField.CompareForXml);
         return copierFieldsForXml;
      }
    

    【讨论】:

      【解决方案3】:

      TL;DR

          orderby typeof(T).Equals(mi.DeclaringType) ? 1 : -1
      

      会先推送base memberInfo,并保持类中定义的顺序。

      完整答案:

      为了达到相同的目标,并使用之前建议的 DeclaringType,我定义了以下方法:

          public static IEnumerable<MemberInfo> GetAllFieldsAndPropertiesOfClass<T>()
          {
              return
                  from mi in typeof(T).GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
                  let ignoreAttr = (IgnoreSerializationAttribute)Attribute.GetCustomAttribute(mi, typeof(IgnoreSerializationAttribute))
                  where (mi.MemberType == MemberTypes.Field || mi.MemberType == MemberTypes.Property)
                     && (ignoreAttr == null || ignoreAttr != null && !ignoreAttr.Ignore)
                  orderby typeof(T).Equals(mi.DeclaringType) ? 1 : -1
                  select mi;
          }
      

      在那个方法中,我还定义了一个自定义属性来明确忽略序列化中的一些属性:

      [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
      public class IgnoreSerializationAttribute : Attribute
      {
          public bool Ignore { get; private set; }
      
          public IgnoreSerializationAttribute(bool ignore)
          {
              Ignore = ignore;
          }
      }
      

      也可以添加其他自定义属性来定义顺序,例如

      [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
      public class ColumnOrderAttribute : Attribute
      {
          public int Order { get; private set; }
      
          public ColumnOrderAttribute(int order)
          {
              Order = order;
          }
      }
      

      用法如下:

          public static IEnumerable<MemberInfo> GetAllFieldsAndPropertiesOfClassOrdered<T>()
          {
              return
                  from mi in GetAllFieldsAndPropertiesOfClass<T>()
                  let orderAttr = (ColumnOrderAttribute)Attribute.GetCustomAttribute(mi, typeof(ColumnOrderAttribute))
                  orderby orderAttr == null ? int.MaxValue : orderAttr.Order, mi.Name
                  select mi;
          }
      

      我正在使用这些方法将使用其他对象的对象列表序列化为 CSV 文件...

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-08-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-19
        • 2016-05-10
        • 2010-11-14
        • 1970-01-01
        相关资源
        最近更新 更多