Solved

Advanced Reflection with Generics - nested IList

Posted on 2008-10-07
12
3,489 Views
Last Modified: 2013-12-17
I am trying to work with reflection and am trying to create a helper method for my unit tests to print out all the properties of an object.  Some objects have nested collections - of type IList - and I want to be able to iterate through those as well.

The code snippet below is what I'm using.  The first line in the 2nd foreach loop is where I'm trying to determine if the property I'm inspecting is an IList - and if it is - I want to make the recursive call.
------------
     foreach (PropertyInfo p in props)
                {
                    //check to see if its of type IList and make a recursive call
                    if (p.PropertyType.IsGenericType)
                    {
                             //  recursive call to  PrintListProperties<p.type?>((IList)p,"       ");
                    }


------------
How could I go about doing this? I've tried a couple things but can't seem to figure it out.




public static void PrintListProperties<T>(IList<T> list, string indent)
        {
            PropertyInfo[] props;
            string s = "";
            string padding = "";
 
            foreach (var t in list)
            {
                props = t.GetType().GetProperties();
                
                Console.WriteLine(indent + "\n-------New Property---------");
                foreach (PropertyInfo p in props)
                {
                    //check to see if its of type IList and make a recursive call
                    if (p.PropertyType.IsGenericType)
                    {
                    }
 
                    s = string.Format("{0}={1} ", p.Name, p.GetValue(t, null));
                    padding = "";
                    if (s.Length < 20)
                    {
                        for (int i = 0; i < 20 - s.Length; i++)
                            padding += " ";
                    }
                    else
                    {
                        padding = "\n";
                    }
 
                    Console.Write(indent + s + padding);
                }
            }
        }

Open in new window

0
Comment
Question by:bswiftly
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 5
  • 4
  • 3
12 Comments
 
LVL 55

Expert Comment

by:Jaime Olivares
ID: 22666178
Have a look to this code snippet:

         object obj = ......;   // some object inherited from IList
   
          System.Reflection.PropertyInfo countprop = obj.GetType().GetProperty("Count", typeof(int));
          if (countprop == null)
                return;

          int count = (int)countprop.GetValue(obj, null);
          if (count<1)
                 return;

          System.Reflection.PropertyInfo indexer = obj.GetType().GetProperty("Item", new Type[] { typeof(int) });
          if (indexer == null)
                return;

          for (int i = 0; i<count; i++)
          {
              object value = indexer.GetValue(obj, new object[] { i });
              // do something with value
          }
0
 
LVL 1

Author Comment

by:bswiftly
ID: 22666304
ok, so that isn't working for me.

I created a method with the code above, and the first line always returns null and skips out of the function.

This is my object roughly -

public class User()
{
       public FirstName {get;set;}
      public IList<Task> Tasks {get;set;}
}

So when i get to the Tasks (or for other objects with a generic list of different type) I'd like to print out all the properties of each Task object.


0
 
LVL 55

Expert Comment

by:Jaime Olivares
ID: 22666341
which element is obj?:
User
Firstname
Tasks
0
Prepare for your VMware VCP6-DCV exam.

Josh Coen and Jason Langer have prepared the latest edition of VCP study guide. Both authors have been working in the IT field for more than a decade, and both hold VMware certifications. This 163-page guide covers all 10 of the exam blueprint sections.

 
LVL 1

Author Comment

by:bswiftly
ID: 22666345
Tasks.

It is 'p' in the 2nd foreach loop (the PropertyInfo).

0
 
LVL 55

Expert Comment

by:Jaime Olivares
ID: 22666368
can you debug where the function returns?
does Tasks have some contents?
0
 
LVL 1

Author Comment

by:bswiftly
ID: 22666403
Yes, I've been stepping through it.  There is no property  "Count" on that object.

0
 
LVL 4

Expert Comment

by:Busac
ID: 22671826
To rely on the Count property is not a good idea because:

1. Types that don't implement IList<> may have a property called Count.
2. Types that do implement IList<> may not have a property called Count. (They could implement IList<>.Count explicitly.)

To find out if the p.PropertyType is IList<> or implements IList<>, you can do the following:

if ((p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)) || p.PropertyType.GetInterface("IList`1") != null) {
    // recursive calls
}

The first part checks if PropertyType is IList<> and the second part checks if PropertyType implements IList<>. (The second part returns false if PropertyType is IList<>, that's why you need the first check.)

The next step would be getting the value of the property and casting it to IList<T>. For this you need to figure out what type T needs to be, I would advise on looking into the GetGenericArguments() method or maybe the GetInterfaceMap() method, I'm not sure how to do this yet but I'll be back in a while if you won't be able to continue.
0
 
LVL 4

Expert Comment

by:Busac
ID: 22672247
Also one thing to clarify - suppose you have an instance of the following type:

public class MyClass()
{
    public object Data {get;set;}
}

and you assign an instance of IList<> to its Data property:

MyClass c = new MyClass();
c.Data = new List<string>();

Do you want your PrintListProperties method to make a recursive call on the Data property or not? In other words, do you only want to check the declared return type of the property or also check the runtime type of the property value?
0
 
LVL 1

Author Comment

by:bswiftly
ID: 22673888
Thanks - I haven't figured this out yet but looks like a better solution.

I don't really care about that example you showed - I'm really only looking for the declared type - as these are all for business objects and there won't be any 'object' objects hanging around.

Thanks for the help so far, hopefully you have some time to continue - I'll let you know if I come up with something.
0
 
LVL 4

Accepted Solution

by:
Busac earned 100 total points
ID: 22674361
Ok I'm back. So first of all, the code is a bit simpler if we only look at the actual runtime type of the property value and since you don't care about the difference, I'll do it this way. A quick note - you can't call PrintListProperties<> directly because it is a generic method and you don't know what generic arguments this method takes at compile time. Because of this, we will have to make the recursive call using reflection. The code would look something like this:

foreach (PropertyInfo p in props)
{
    // We need to distinguish between indexed properties and parameterless properties
    if (p.GetIndexParameters().Length == 0)
    {
        // This is the value of the property
        Object v = p.GetValue(t, null);
        // If it implements IList<> ...
        if (v != null && v.GetType().GetInterface("IList`1") != null)
        {
            // ... then make the recursive call:
            typeof(YourDeclaringClassHere).GetMethod("PrintListProperties").MakeGenericMethod(v.GetType().GetInterface("IList`1").GetGenericArguments()).Invoke(null, new object[] { v, indent + "  " });
        }
        Console.WriteLine(indent + "  {0} = {1}", p.Name, v);
    }
    else
    {
        Console.WriteLine(indent + "  {0} = <indexed property>", p.Name);
    }
}

Open in new window

0
 
LVL 1

Author Comment

by:bswiftly
ID: 22675859
Hey thanks..

That works - the only thing I had to change was my declaring class of course, and then I had overridden the PrintListProperties method so it was returning 2 objects not just one and I had to get rid of the overriden method.

Thanks man..  I appreciate the help!
0
 
LVL 4

Expert Comment

by:Busac
ID: 22676487
No problem, I'm happy to hear it worked. If you have any related problems don't hesitate to post here ;-).
0

Featured Post

NEW Veeam Agent for Microsoft Windows

Backup and recover physical and cloud-based servers and workstations, as well as endpoint devices that belong to remote users. Avoid downtime and data loss quickly and easily for Windows-based physical or public cloud-based workloads!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This document covers how to connect to SQL Server and browse its contents.  It is meant for those new to Visual Studio and/or working with Microsoft SQL Server.  It is not a guide to building SQL Server database connections in your code.  This is mo…
One of Google's most recent algorithm changes affecting local searches is entitled "The Pigeon Update." This update has dramatically enhanced search inquires for the keyword "Yelp." Google searches with the word "Yelp" included will now yield Yelp a…
I've attached the XLSM Excel spreadsheet I used in the video and also text files containing the macros used below. https://filedb.experts-exchange.com/incoming/2017/03_w12/1151775/Permutations.txt https://filedb.experts-exchange.com/incoming/201…
Michael from AdRem Software explains how to view the most utilized and worst performing nodes in your network, by accessing the Top Charts view in NetCrunch network monitor (https://www.adremsoft.com/). Top Charts is a view in which you can set seve…

623 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question