递归获取属性&一类的儿童属性

时间:2013-12-12 20:57:42

标签: c# reflection recursion

我正在做Recursively Get Properties & Child Properties Of An Object之类的事情,但我希望以递归方式使用反射来获取每个属性。我从Recursively Print the properties获得了代码。

代码的问题是:它只降低了一级,我想知道如何使用反射自动获取所有属性?我刚刚编写了以下示例容器代码:

public class Container
{
    public Bottle MyBottle { get; set; }
    public List<Address> Addresses { get; set; }

    public Container()
    {
        Address a = new Address();
        a.AddressLine1 = "1 Main St";
        a.AddressLine2 = "2 Main St";
        Addresses = new List<Address>();
        Addresses.Add(a);

        MyBottle = new Bottle();
        MyBottle.BottleName = "Big bottle";
        MyBottle.BottageAge = 2;
    }
}

public class Bottle
{
    public string BottleName { get; set; }
    public int BottageAge { get; set; }
}

public class Address
{
    public string AddressLine1 { get; set; }
    public string AddressLine2 { get; set; }
    public List<SpecialFolder> SpecialFolders { get; set; }

    public Address()
    {
        SpecialFolders = new List<SpecialFolder>();
        SpecialFolder sf = new SpecialFolder();
        sf.TemplateFolder = Environment.SpecialFolder.Templates.ToString();
        sf.UserFolder = Environment.SpecialFolder.UserProfile.ToString();
        SpecialFolders.Add(sf);
    }
}

public class SpecialFolder
{
    public string TemplateFolder { get; set; }
    public string UserFolder { get; set; }
}

在Main方法中:

static void Main(string[] args)
{
    Container c = new Container();
    PrintProperties(c);
}
public static void PrintProperties(object obj)
{
    PrintProperties(obj, 0);
}
public static void PrintProperties(object obj, int indent)
{

    if (obj == null) return;
    string indentString = new string(' ', indent);
    Type objType = obj.GetType();
    PropertyInfo[] properties = objType.GetProperties();
    foreach (PropertyInfo property in properties)
    {
        object propValue = property.GetValue(obj, null);
        if (property.PropertyType.Assembly == objType.Assembly)
        {
            Console.WriteLine("{0}{1}:", indentString, property.Name);

            PrintProperties(propValue, indent + 2);
        }
        else
        {
            Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue);
        }
    }
}

我希望得到:

MyBottle:
      BottleName: Big bottle
      BottageAge: 2
Addresses:
      AddressLine1: 1 Main St
      AddressLine2: 2 Main St
      SpecialFolders:
            TemplateFolder: Templates
            UserFolder: UserProfile

我现在得到的结果:

MyBottle:
  BottleName: Big bottle
  BottageAge: 2
Addresses: System.Collections.Generic.List`1[TreeViewReflectionExample.Address]

有人可以帮我使用PrintProperties方法吗?非常感谢你。

6 个答案:

答案 0 :(得分:47)

您的代码有两个问题:

  1. 由于条件if (property.PropertyType.Assembly == objType.Assembly),您将省略System.Collections List<>
  2. 您不会区别对待propValue作为集合。因此,它将打印List属性,而不是其元素属性。
  3. 您可以将其更改为:

    public void PrintProperties(object obj, int indent)
    {    
        if (obj == null) return;
        string indentString = new string(' ', indent);
        Type objType = obj.GetType();
        PropertyInfo[] properties = objType.GetProperties();
        foreach (PropertyInfo property in properties)
        {
            object propValue = property.GetValue(obj, null);
            var elems = propValue as IList;
            if (elems != null)
            {
                foreach (var item in elems)
                {
                    PrintProperties(item, indent + 3);
                }
            }
            else
            {
                // This will not cut-off System.Collections because of the first check
                if (property.PropertyType.Assembly == objType.Assembly)
                {
                    Console.WriteLine("{0}{1}:", indentString, property.Name);
    
                    PrintProperties(propValue, indent + 2);
                }
                else
                {
                    Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue);
                }
            }
        }
    }
    

答案 1 :(得分:12)

你想分别处理原始类型和字符串,并循环遍历可枚举而不是仅仅获取它们的ToString()值。因此,您的代码可以更新为:

public void PrintProperties(object obj, int indent)
{

    if (obj == null) return;
    string indentString = new string(' ', indent);
    Type objType = obj.GetType();
    PropertyInfo[] properties = objType.GetProperties();
    foreach (PropertyInfo property in properties)
    {   
        object propValue = property.GetValue(obj, null);
        if(property.PropertyType.IsPrimitive || property.PropertyType == typeof(string))
            Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue);
        else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
        {
            Console.WriteLine("{0}{1}:", indentString, property.Name);
            IEnumerable enumerable = (IEnumerable)propValue;
            foreach(object child in enumerable)
                PrintProperties(child, indent + 2);
        }
        else 
        {
            Console.WriteLine("{0}{1}:", indentString, property.Name);
            PrintProperties(propValue, indent + 2);
        }
    }
}

答案 2 :(得分:1)

除了propValue是string []之外,它适用于所有情况。您将获得行中的“参数计数不匹配”异常:     object propValue = property.GetValue(obj,null);

要解决此问题,您可以使用此代码进行一些修复:

<?php
    $layer = Mage::getModel("catalog/layer");
    $category = Mage::getModel('catalog/category')->load($this->getCategory());
    $layer->setCurrentCategory($category);
    $attributes = $layer->getFilterableAttributes();

    foreach ($attributes as $attribute) {
        if ($attribute->getAttributeCode() == 'color') {
            $filterBlockName = 'catalog/layer_filter_attribute';
            $result = Mage::app()->getLayout()->createBlock($filterBlockName)->setLayer($layer)->setAttributeModel($attribute)->init();
            echo '<strong>Color:</strong><br />';

            foreach($result->getItems() as $option) {
               echo '&nbsp; &nbsp; &nbsp; <a href="' . $category->getUrl() . '/?color=' . $option->getValue() . '">' . $option->getValue() . ' - ' . $option->getLabel() . '</a><br />';
            }
        }
    }
?>

答案 3 :(得分:1)

基于Konrad Kokosa的回答:

private string ObjectToString(object obj, int indent = 0)
{
    if (obj is null)
    {
        return "";
    }

    var sb = new StringBuilder();
    string indentString = new string(' ', indent);
    Type objType = obj.GetType();

    foreach (PropertyInfo property in objType.GetProperties())
    {
        object propValue = property.GetValue(obj);
        var elems = propValue as IList;

        if (elems != null)
        {
            foreach (var item in elems)
            {
                sb.Append($"{indentString}- {property.Name}\n");
                sb.Append(ObjectToString(item, indent + 4));
            }
        }
        else if (property.Name != "ExtensionData")
        {
            sb.Append($"{indentString}- {property.Name}={propValue}\n");

            if (property.PropertyType.Assembly == objType.Assembly)
            {
                sb.Append(ObjectToString(propValue, indent + 4));
            }
        }
    }

    return sb.ToString();
}

更新

根据以下较早的问题编辑代码:TargetParameterCountException when enumerating through properties of string

private string ObjectToString(object obj, int indent = 0)
    {
        var sb = new StringBuilder();

        if (obj != null)
        {
            string indentString = new string(' ', indent);

            if (obj is string)
            {
                sb.Append($"{indentString}- {obj}\n");
            }
            else if (obj is Array)
            {
                var elems = obj as IList;
                sb.Append($"{indentString}- [{elems.Count}] :\n");

                for (int i = 0; i < elems.Count; i++)
                {
                    sb.Append(ObjectToString(elems[i], indent + 4));
                }
            }
            else
            {
                Type objType = obj.GetType();
                PropertyInfo[] props = objType.GetProperties();

                foreach (PropertyInfo prop in props)
                {
                    if (prop.GetIndexParameters().Length == 0)
                    {
                        object propValue = prop.GetValue(obj);
                        var elems = propValue as IList;

                        if (elems != null)
                        {
                            foreach (var item in elems)
                            {
                                sb.Append($"{indentString}- {prop.Name} :\n");
                                sb.Append(ObjectToString(item, indent + 4));
                            }
                        }
                        else if (prop.Name != "ExtensionData")
                        {
                            sb.Append($"{indentString}- {prop.Name} = {propValue}\n");

                            if (prop.PropertyType.Assembly == objType.Assembly)
                            {
                                sb.Append(ObjectToString(propValue, indent + 4));
                            }
                        }
                    }
                    else
                    {
                        sb.Append($"{indentString}- {prop.Name} ({prop.PropertyType.Name}): <Indexed>\n");
                    }
                }
            }
        }

        return sb.ToString();
    }

答案 4 :(得分:1)

我已经花了很多时间来解决这个问题。要正确转储所有属性类型,需要使用许多特定于类型的格式。

我建议您看看github上此文件中的FormatValue方法。这是将属性值写入字符串的主要逻辑: https://github.com/thomasgalliker/ObjectDumper/blob/ada64c7e51fedf57731006959358aa890b5e4344/ObjectDumper/Internal/ObjectDumperCSharp.cs#L98

您还可以将此代码用作nuget包: https://www.nuget.org/packages/ObjectDumper.NET

答案 5 :(得分:-2)

我改变了传言的代码。它对我有用。

public void PrintProperties(object obj, int indent)
{
    if (obj == null)
    {
        return;
    }
    string indentString = new string(' ', indent);
    Type objType = obj.GetType();
    PropertyInfo[] properties = objType.GetProperties();
    foreach (PropertyInfo property in properties)
    {
        object propValue = property.GetValue(obj, null);
        if (IsSimpleType(property.PropertyType))
        {
            Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue);
        }
        else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
        {
            if (property.PropertyType == typeof(string[]))
            {
                Console.WriteLine("{0}{1}: {2}", indentString, property.Name, string.Join(",", (string[])propValue));
            }
            else
            {
                Console.WriteLine("{0}{1}:", indentString, property.Name);
                IEnumerable enumerable = (IEnumerable)propValue;
                foreach (object child in enumerable)
                {
                    PrintProperties(child, indent + 2);
                }
            }
        }
        else
        {
            Console.WriteLine("{0}{1}:", indentString, property.Name);
            PrintProperties(propValue, indent + 2);
        }
    }
}

public static bool IsSimpleType(Type type)
{
    return
        type.IsValueType ||
        type.IsPrimitive ||
        new Type[]
        { 
            typeof(String),
            typeof(Decimal),
            typeof(DateTime),
            typeof(DateTimeOffset),
            typeof(TimeSpan),
            typeof(Guid)
        }.Contains(type) ||
        Convert.GetTypeCode(type) != TypeCode.Object;
}