Рекурсивно Получить свойства и дочерние свойства класса

Я делал что-то вроде Рекурсивно Получить свойства и дочерние свойства объекта, но я хотел использовать рекурсивные рефлексы для получения каждого свойства. И я получил код Рекурсивно распечатать свойства.

Проблема с кодом: он идет только на один уровень вниз, интересно, как вы можете автоматически получить все свойства, используя отражение? Я просто составил следующий пример кода контейнера:

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; }
}

В основном методе:

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? Большое вам спасибо.

Ответ 1

У вас есть две проблемы с вашим кодом:

  • из-за условия if (property.PropertyType.Assembly == objType.Assembly) вы опустите System.Collections как List<>
  • вы не относитесь иначе propValue, которые являются коллекциями. Следовательно, он будет печатать свойства List, а не свойства его элементов.

Вы можете изменить это, например, на:

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);
            }
        }
    }
}

Ответ 2

Вы хотите обрабатывать примитивные типы и строки отдельно и перебирать перечислимые числа вместо того, чтобы просто принимать их значение 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);
        }
    }
}

Ответ 3

Он работает для всех случаев, кроме propValue является строкой []. Вы получите исключение "Несоответствие счетчика параметров" в строке:   object propValue = property.GetValue(obj, null);

Чтобы устранить эту проблему, вы можете использовать этот код с небольшим исправлением:

private 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) && !(elems is string[]) )
            {
                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)
                {
                    LogToWindow(String.Format("{0}{1}:", indentString, property.Name));
                    PrintProperties(propValue, indent + 2);
                }
                else
                {
                    if (propValue is string[])
                    {
                        var str = new StringBuilder();
                        foreach (string item in (string[])propValue)
                        {
                            str.AppendFormat("{0}; ", item);
                        }
                        propValue = str.ToString();
                        str.Clear();
                    }
                    LogToWindow(String.Format("{0}{1}: {2}", indentString, property.Name, propValue));
                }
            }
        }
    }

Ответ 4

На основании ответа Конрада Кокоса:

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 при перечислении через свойства строки

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();
    }

ОБНОВЛЕНИЕ 2

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

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

            if (obj is string || obj.IsNumber())
            {
                sb.Append($"{indentString}- {obj}\n");
            }
            else if (obj.GetType().BaseType == typeof(Enum))
            {
                sb.Append($"{indentString}- {obj.ToString()}\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 if (objType.GetProperty("Item") != null)
                    {
                        int count = -1;

                        if (objType.GetProperty("Count") != null &&
                            objType.GetProperty("Count").PropertyType == typeof(int))
                        {
                            count = (int)objType.GetProperty("Count").GetValue(obj, null);
                        }

                        for (int i = 0; i < count; i++)
                        {
                            object val = prop.GetValue(obj, new object[] { i });
                            sb.Append(ObjectToString(val, indent + 4));
                        }
                    }
                }
            }
        }

        return sb.ToString();
    }

    public static bool IsNumber(this object value)
    {
        return value is sbyte
            || value is byte
            || value is short
            || value is ushort
            || value is int
            || value is uint
            || value is long
            || value is ulong
            || value is float
            || value is double
            || value is decimal;
    }

Ответ 5

Этот метод создает карту объекта.

Карта - это словарь, в котором TKey - строка, представляющая имя свойства, а TValue - объект (коллекция или значение).

public static class Extensions 
{
        public static void ToTypeMap<T>(this T type, ref Dictionary<string, object> map)
        {
            if(IsValue(type))
            {
                return;
            }

            Type reflectedType = type.GetType();

            PropertyInfo[] properties = reflectedType.GetProperties();


            foreach (PropertyInfo property in properties)
            {
                string key = property.Name;
                object @object = property.GetValue(type, null); /// => type to check in case of <else>


                if (IsValue(property))
                {
                    if (map.ContainsKey(key)) continue;

                    map.Add(key, @object);
                }

                else /// => property is reference type.
                {
                    if (@object == null) /// => object can be null.
                    {
                       // if (!map.ContainsKey(key)) map.Add(key, null); /// => uncomment this line if you want to map the property with a value of null. 

                        continue;
                    }

                    /// => get all properties for this reference type.
                    PropertyInfo[] innerProperties = @object.GetType().GetProperties();

                    /// => create the inner map 
                    Dictionary<string, object> innerMap = new Dictionary<string, object>();


                    if (IsCollection(property))
                    {
                        List<object> items = new List<object>();

                        foreach (object item in (System.Collections.IEnumerable)@object)
                        {
                            ToTypeMap(item, ref innerMap);

                            items.Add(item);
                        }

                        if (map.ContainsKey(key)) continue;

                        map.Add(key, items);
                    }
                    else
                    {
                        foreach (PropertyInfo innerProperty in innerProperties)
                        {
                            ToTypeMap(@object, ref innerMap);
                        }

                        if (map.ContainsKey(key)) continue;

                        map.Add(key, innerMap);
                    }
                }
            }
        }


    [DebuggerStepThrough]
    private static bool IsCollection(PropertyInfo property) => typeof(System.Collections.IEnumerable).IsAssignableFrom(property.PropertyType) || property.PropertyType.IsArray;

    [DebuggerStepThrough]
    private static bool IsValue(PropertyInfo property) => property.PropertyType.IsValueType || property.PropertyType == typeof(string);

    [DebuggerStepThrough]
    private static bool IsValue(object @object) => @object.GetType().IsValueType || @object.GetType() == typeof(string);


}

пример

class Program
{
    static void Main(string[] args)
    {
        Dictionary<string, object> myMap = new Dictionary<string, object>();
        Dictionary<string, object> yourMap = new Dictionary<string, object>();

        Outer outer = new Outer
        {
            Id = 1,
            Value = 2,
            Inner = new Inner
            {
                Id = "value",
                Values = new string[] { "a", "b", "a", "b", "a", "b", "a", "b", "a", "b", "a", "b", },
                InnerInner = new InnerInner
                {
                    Id = 3,
                    Collecton = new List<object> { 1, 2, 5.5m, "a", "a", "a" }
                }
            },
            Ids = new Prop[] { new Prop { Id = 99 }, new Prop { Id = 98 } },
            Map = new Dictionary<string, string>
            {
                {"123", "asd" },
                {"000", "zxc" },
            }
        };

        Container container = new Container();

        container.ToTypeMap(ref yourMap);

        outer.ToTypeMap(ref myMap);
    }
}

Граф объектов.

public class Outer
{
    public int Id { get; set; }
    public int Value { get; set; }

    public Inner Inner { get; set; }

    public Prop[] Ids { get; set; }

    public Dictionary<string, string> Map { get; set; }
}

public class Prop
{
    public int Id { get; set; }
}

public class Inner
{
    public string Id { get; set; }
    public InnerInner InnerInner { get; set; }
    public string[] Values { get; set; }
}

public class InnerInner
{
    public int Id { get; set; }
    public List<object> Collecton { get; set; }
}

Ответ 6

Я изменил код driis ниже. Это работает для меня.

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;
}