反射,循环遍历类层次结构并按类型查找字段

时间:2012-01-02 11:03:25

标签: c# reflection

我有一个大的层次结构类,我想循环所有它的属性和子属性等。

实施例

public class RootClass
{
   // properties ..
   List<Item> FirstItemsList { get; set;}
   List<Item> SecondItemsList { get; set;}
   SubClass SubClass { get; set;}
}
public class SubClass
{
   // properties ..
   List<Item> ThirdItemsList { get; set;}
}
public class Item
{
   //properties 
}

我想要一个函数,它会返回一个找到的所有Item类型的列表 即

public IList<Item> GetAllItemsInClass(RootClass entity);

由于

1 个答案:

答案 0 :(得分:0)

如果您需要能够在一般情况下工作的东西(任何类层次结构),那么您可以执行以下操作:

您需要一个递归算法(函数)。该算法将循环遍历成员,将其类型添加到列表中(如果它们尚未添加)然后返回该列表,COMBINED与刚添加到列表中的类型成员的类型 - 在此处进行递归呼叫。终止条件是:

  1. 成员的类型是基元(要检查此用途Type.IsPrimitive)。
  2. 反映的类型在另一个程序集中定义。您可以使用Type.Assembly
  3. 检查定义装配

    如果你需要更简单的东西,那么使用上面的技术,但只需在循环中使用if语句。

    让我知道这是你想要的还是没有,然后我会在你有更多时间的时候为你发布一个代码示例。

    更新:以下是一个代码示例,展示了如何处理类型并递归获取其中包含的所有类型。您可以这样称呼它:List typesHere = GetTypes(myObject.GetType())

        public static List<Type> GetTypes(Type t)
        {
            List<Type> list = new List<Type>();
            if (t.IsPrimitive)
            {
                if (!list.Contains(t))
                    list.Add(t);
                return list;
            }
            else if (!t.Assembly.Equals(System.Reflection.Assembly.GetExecutingAssembly()))
            {
                //if the type is defined in another assembly then we will check its
                //generic parameters. This handles the List<Item> case.
                var genArgs = t.GetGenericArguments();
                if (genArgs != null)
                    foreach (Type genericArgumentType in genArgs)
                    {
                        if(!list.Contains(genericArgumentType))
                            list.AddRange(GetTypes(genericArgumentType));
                    }
                return list;
            }
            else
            {
                //get types of props and gen args
                var types = t.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Select(pi => pi.PropertyType).ToList();
                types.AddRange(t.GetGenericArguments());
                foreach (System.Type innerType in types)
                {
                    //get the object represented by the property to traverse the types in it.
                    if (!list.Contains(innerType))
                        list.Add(innerType);
                    else continue; //because the type has been already added and as thus its child types also has been already added.
    
                    var innerInnerTypes = GetTypes(innerType);
                    //add the types filtering duplicates
                    foreach (Type t1 in innerInnerTypes) //list.AddRange(innerTypes); //without filtering duplicates.
                        if (!list.Contains(t1))
                            list.Add(t1);
                }
                return list;
            }
        }
    

    所以,当我在您原始帖子中发布的类上运行此项目时(项目有两个原始属性,如下所示),我得到以下列表:

    GetTypes(typeof(List<Item>))
    Count = 3
        [0]: {Name = "Item" FullName = "AssemblyNameXYZ.Item"}
        [1]: {Name = "String" FullName = "System.String"}
        [2]: {Name = "Int32" FullName = "System.Int32"}
    
    GetTypes(typeof(Item))
    Count = 2
        [0]: {Name = "String" FullName = "System.String"}
        [1]: {Name = "Int32" FullName = "System.Int32"}
    
    Reflection.GetTypes(typeof(RootClass))
    Count = 5
        [0]: {Name = "List`1" FullName = "System.Collections.Generic.List`1[[AssemblyNameXYZ.Item, AssemblyNameXYZ, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"}
        [1]: {Name = "Item" FullName = "AssemblyNameXYZ.Item"}
        [2]: {Name = "String" FullName = "System.String"}
        [3]: {Name = "Int32" FullName = "System.Int32"}
        [4]: {Name = "SubClass" FullName = "AssemblyNameXYZ.SubClass"}
    

    我没有进行全面测试,但这至少应该指向正确的方向。有趣的问题。我很开心回答。