reflection on List and printing values

2019-06-20 04:28发布

问题:

I wrote a method that accepts a generic parameter and then it prints its properties. I use it to test my web service. It's working but I want to add some features that I don't know how to implement. I want to print values of lists, because now it just writes System.Collection.Generic.List1 which is expected.

Here is my code so far, this is working for basic types (int, double etc.):

static void printReturnedProperties<T>(T Object)
{ 
   PropertyInfo[] propertyInfos = null;
   propertyInfos = Object.GetType().GetProperties();

   foreach (var item in propertyInfos)
      Console.WriteLine(item.Name + ": " + item.GetValue(Object).ToString());
}

回答1:

You could do something like this:

    static void printReturnedProperties(Object o)
    {
        PropertyInfo[] propertyInfos = null;
        propertyInfos = o.GetType().GetProperties();



        foreach (var item in propertyInfos)
        {
            var prop = item.GetValue(o);

            if(prop == null)
            {
                Console.WriteLine(item.Name + ": NULL");
            }
            else
            {
                Console.WriteLine(item.Name + ": " + prop.ToString());
            }


            if (prop is IEnumerable)
            {
                foreach (var listitem in prop as IEnumerable)
                {
                    Console.WriteLine("Item: " + listitem.ToString());
                }
            }
        }


    }

It will then enumerate through any IEnumerable and print out the individual values (I'm printing them one per line, but obviously, you can do different.)



回答2:

The elements inside a list can be retrieved through the indexer property Item. This property accepts an index argument (there is an overload of PropertyInfo.GetValue that accept an object array, just like MethodInfo.Invoke) and returns the object at that position.

int index = /* the index you want to get here */;
PropertyInfo indexer = Object.GetProperty("Item");
object item = indexer.GetValue(Object, new object[] { index });


回答3:

I usually prints list with a , between each item.

To make that easy I have created a simple extension method:

public static class ListEx
{
    public static string StringJoin<T>(this IEnumerable<T> items)
    {
        return string.Join(", ", items);
    }
}

Call the method as myList.StringJoin().

You can of course modify the method to use another delimiter och call string.Join directly.



回答4:

Here is a snippet, assuming that your List is of Type T.

 foreach (PropertyInfo item in propertyInfos)
            {
                Object obj = item.GetValue(object,null);
                if (!obj.GetType().IsValueType)
                {
                    if (obj.GetType() == typeof(String))
                    {
                        Console.WriteLine(obj.ToString());
                    }
                    else if (obj.GetType() == typeof(List<T>))
                    {
                        //run a loop and print the list

                    }
                    else if (obj.GetType().IsArray) // this means its Array
                    {
                        //run a loop to print the array
                    }

                }
                else
                {
                    //its primitive so we will convert to string 
                    Console.WriteLine(obj.ToString());

                }


回答5:

I think you want something like this:

public class Program
{
    public static void PrintProperties<T>(T t)
    {
        var properties = t.GetType().GetProperties();

        foreach (var property in properties)
        {
            var name = property.Name;
            var value = property.GetValue(t, null);

            if (property.PropertyType.IsGenericType && property.PropertyType == typeof(IEnumerable<>))
            {
                var formatList = typeof(Program).GetMethod("FormatList", new[] { value.GetType() });

                // value.GetType().GetGenericArguments().First() will get you the underlying type of the list,
                // i.e., the TItemType where the property you are currently
                // handling is of type IEnumerable<TItemType>
                formatList.MakeGenericMethod(value.GetType().GetGenericArguments().First());

                value = formatList.Invoke(null, new object[] { value });

                Console.Out.WriteLine(name + ": " + value);
            }
            else
            {
                Console.Out.WriteLine(name + ": " + value);
            }
        }
    }

    public static string FormatList<TPlaceholder>(IEnumerable<TPlaceholder> l)
    {
        return string.Join(", ", l);
    }
}

The code is untested but basically, you want to tackle enumerable types differently as compared to scalar values, so once you hit something of the type IEnumerable<TItemType>, you make a call to the FormatList<TPlaceholder> method.

Now, bear in mind that your original T and TItemType are not necessarily the same. When you invoke FormatList using reflection, you want to bind the TPlaceholder to TItemType. Once you have done that, you just invoke the formatting method and pass it the actual instance of the list, which returns you a string. That string you can then just output.

Hope that helps.



回答6:

Borrowing heavily on the above examples, here is my full solution. This has been tested and handles IEnumerable's being passed in by printing out the properties of each element. It's not recursive but that's easy enough to add.

Be aware that many of the above examples will crash with indexed properties (lists for example). Parameter count mismatch in property.GetValue().

It's avoided here by filtering properties which have indexed properties using this bit of LINQ.

Where(x=>!x.GetIndexParameters().Any())

Full example in the form of an extension method below.

    /// <summary>
    /// Returns string representation of object property states i.e. Name: Jim, Age: 43
    /// </summary>
    public static string GetPropertyStateList(this object obj)
    {
        if (obj == null) return "Object null";

        var sb = new StringBuilder();
        var enumerable = obj as IEnumerable;

        if (enumerable != null)
        {
            foreach (var listitem in enumerable)
            {
                sb.AppendLine();
                sb.Append(ReadProperties(listitem));
            }
        }
        else
        {
            sb.Append(ReadProperties(obj));
        }

        return sb.ToString();
    }

    private static string ReadProperties(object obj)
    {
        var sb = new StringBuilder();
        var propertyInfos = obj.GetType().GetProperties().OrderBy(x => x.Name).Where(x=>!x.GetIndexParameters().Any());

        foreach (var prop in propertyInfos)
        {
            var value = prop.GetValue(obj, null) ?? "(null)";
            sb.AppendLine(prop.Name + ": " + value);
        }

        return sb.ToString();
    }


标签: c# reflection