8

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());
}
Omar
  • 16,329
  • 10
  • 48
  • 66
gorgi93
  • 2,457
  • 8
  • 30
  • 53
  • Why is that method generic (with a `` type parameter) and you're never using the `T` for anything? – Federico Berasategui Mar 05 '13 at 21:16
  • @HighCore he could do `PrintReturnedProperties(subclass);` – p.s.w.g Mar 05 '13 at 21:17
  • 5
    You should use a parameter name other than `Object` to save yourself from confusion with the built in object/Object. – Umar Farooq Khawaja Mar 05 '13 at 21:17
  • You should also avoid using PascalCased names for variables. The .NET naming conventions reserve that for types. – zneak Mar 05 '13 at 21:19
  • I have edited your title. Please see, "[Should questions include “tags” in their titles?](http://meta.stackexchange.com/questions/19190/)", where the consensus is "no, they should not". – John Saunders Mar 05 '13 at 21:20
  • 2
    @p.s.w.g I don't see how that would be different from removing the type parameter completely and just doing `PrintReturnedProperties(subclass)`. His method is NOT using the T paremeter at all!! – Federico Berasategui Mar 05 '13 at 21:21
  • @HighCore You're right. The way the OP implemented it, it wouldn't be. But if you replaced `Object.GetType()` with `typeof(T)`, the generic parameter would actually be somewhat useful (by ignoring properties of the subclass). – p.s.w.g Mar 05 '13 at 21:31
  • @p.s.w.g that is precisely my point. see my first comment. – Federico Berasategui Mar 05 '13 at 21:33
  • Well i dont know the input type so i must use generics isnt that right? I mean I could also made object parameter but isn't this more elegant solution? – gorgi93 Mar 05 '13 at 21:42
  • 1
    @gGololicic - no, in your example you don't need generics. `GetType` is a method on any object. Changing your signature to `static void printReturnedProperties(object Object)` yields the same thing. This is why other comments suggest to change the name of the parameter from `Object` to something else as it is confusing. – Jay Walker Mar 05 '13 at 22:07
  • thank you. I know it is confusing and bed practice. Just wrote this realy quickly. Will change. So you are saying when you dont need to use generics is always better to use object type? – gorgi93 Mar 05 '13 at 22:08
  • 1
    No, I'm definitely not saying always. just in this case, in your code sample it is not necessary. – Jay Walker Mar 05 '13 at 22:13

6 Answers6

11

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.)

David Hope
  • 2,216
  • 16
  • 32
4

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 });
zneak
  • 134,922
  • 42
  • 253
  • 328
2

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.

Albin Sunnanbo
  • 46,430
  • 8
  • 69
  • 108
1

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

                }
Dhawalk
  • 1,257
  • 13
  • 28
1

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.

Umar Farooq Khawaja
  • 3,925
  • 1
  • 31
  • 52
  • My solution is similar to the accepted answer, accept that in the accepted answer, we would be relying on the `ToString()` method giving something sensible, whereas in my version, we have a little more control over that because we can expand `FormatList()` to do something more sophisticated. – Umar Farooq Khawaja Mar 05 '13 at 21:52
0

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();
    }
Community
  • 1
  • 1
CountZero
  • 6,171
  • 3
  • 46
  • 59