简体   繁体   中英

C# Reflection with recursion

I am working on the Reflection , but i am stuck while doing the recursion.

Code :

public class User {
  public string Name;
  public int Number;
  public Address Address;    
}


public class Address {
 public string Street;
 public string State;
 public string Country;
}

now i am printing the values.

 Type t = user.GetType();  
 PropertyInfo[] props = t.GetProperties(); 
 foreach (PropertyInfo prp in props)  
 {  
   if(!prp.GetType().IsPrimitive && prp.GetType().IsClass) 
   {
     // Get the values of the Inner Class.
     // i am stucked over here , can anyone help me with this.

           Type ty = prp.GetType();
           var prpI = ty.GetProperties();
           //var tp = ty.GetType().;
            foreach (var propertyInfo in prpI)
            {
            var value = propertyInfo.GetValue(prp);
            var stringValue = (value != null) ? value.ToString() : "";
            console.WriteLine(prp.GetType().Name + "." + propertyInfo.Name+" Value : " +stringValue);    
            }
   }
   else
   {    
     var value = prp.GetValue(user);   
     var stringValue = (value != null) ? value.ToString() : "";
     console.writeline(user.GetType().Name + "." + prp.Name+" Value : " +stringValue); 
   }
 }

i want to know how to find out whether the property is a class or the primitive. and do the recursion if it is a class.

First of all, if you want to access the properties of a type, ensure you are using a type which has properties:

public class User {
  public string Name{get;set;}
  public int Number{get;set;}
  public Address Address{get;set;}    
}


public class Address {
 public string Street{get;set;}
 public string State{get;set;}
 public string Country{get;set;}
}

Second, prp.GetType() will always return PropertyInfo . You are looking for prp.PropertyType , which will return the Type of the property.

Also, if(!prp.GetType().IsPrimitive && prp.GetType().IsClass) won't work the way you want, because String eg is a class and also not a primitive. Better use prp.PropertyType.Module.ScopeName != "CommonLanguageRuntimeLibrary" .

Last but not least, to use recursion, you actually have to put your code into a method.

Here's a complete example:

IEnumerable<string> GetPropertInfos(object o, string parent=null)
{
    Type t = o.GetType();  
    PropertyInfo[] props = t.GetProperties(BindingFlags.Public|BindingFlags.Instance);
    foreach (PropertyInfo prp in props)  
    {  
        if(prp.PropertyType.Module.ScopeName != "CommonLanguageRuntimeLibrary")
        {
            // fix me: you have to pass parent + "." + t.Name instead of t.Name if parent != null
            foreach(var info in GetPropertInfos(prp.GetValue(o), t.Name))
                yield return info; 
        }
        else
        {    
            var value = prp.GetValue(o);   
            var stringValue = (value != null) ? value.ToString() : "";
            var info = t.Name + "." + prp.Name + ": " + stringValue;
            if (String.IsNullOrWhiteSpace(parent))
                yield return info; 
            else
                yield return parent + "." + info; 
        }
    }
}

Used like this:

var user = new User { Name = "Foo", Number = 19, Address = new Address{ Street="MyStreet", State="MyState",  Country="SomeCountry" }    };
foreach(var info in GetPropertInfos(user))
    Console.WriteLine(info);

it will output

User.Name: Foo
User.Number: 19
User.Address.Street: MyStreet
User.Address.State: MyState
User.Address.Country: SomeCountry

First of all, avoid using reflection unless you really need it. It's slow, it's messy, it's borderline undebuggable (and I love it, but that's another thing)

If you want to dump the whole content of your object, I would recommend to transfer that to the objects themselves, they should know their inner state. You could use the objects ToString method to write their inner state, or you could define an interface for displaying the inner state

interface IStateDisplay
{
   string GetInnerState();
}

and make your objects implement it. Then the code for displaying the properties will be

Console.WriteLine(user.GetInnerState());

Another option would be to use a tool like AutoMapper that hides the complexities and intricacies of reflection from you, and exposes a nice API to use.

However, if you are learning about reflection, printing the state of a complex object is a nice exercise. A few pointers in that direction:

public string Name;

is not a property, it's a field, so type.GetProperties() will not return it. Read up on what C# properties are, and how they are used and defined. A minimal property declaration is

public string Name {get; set;}

Also, prp.GetType() will return the type information for the PropertyInfo type, not for the type of the property it contains. What you need in this case is the prp.PropertyType property.

Next, I don't think the Type.IsPrimitive check is what you want it to be. That property returns true for the Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single types, and false for everything else. Most importantly typeof(string).IsPrimitive returns false.

On the same note, I don't think the Type.IsClass check is what you want it to be, either. As you are using it, that only check if the property is of a value or of a reference type, and as value types ( struct ) can be also very complex and contain properties and fields of their own, the check does not make sense.

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;


namespace Extensions
{
    public static class ObjectExtension
    {
        public static string ToStringProperties(this object o)
        {
            return o.ToStringProperties(0);
        }
    public static string ToStringProperties(this object o, int level)
    {
        StringBuilder sb = new StringBuilder();
        string spacer = new String(' ', 2 * level);
        if (level == 0) sb.Append(o.ToString());
        sb.Append(spacer);
        sb.Append("{\r\n");
        foreach (PropertyInfo pi in o.GetType().GetProperties())
        {
        if (pi.GetIndexParameters().Length == 0)
        {
            sb.Append(spacer);
            sb.Append("  ");
            sb.Append(pi.Name);
            sb.Append(" = ");

            object propValue = pi.GetValue(o, null);
            if (propValue == null)
            {
                sb.Append(" <null>");
            } else {
                if (IsMyOwnType(pi.PropertyType))
                {
                    sb.Append("\r\n");
                    sb.Append(((object)propValue).ToStringProperties(level + 1));
                } else{
                    sb.Append(propValue.ToString());
                }
            }
            sb.Append("\r\n");
        }
    }
    sb.Append(spacer);
    sb.Append("}\r\n");
    return sb.ToString();
}
    private static bool IsMyOwnType(Type t) 
{
    return (t.Assembly == Assembly.GetExecutingAssembly());
}
}
}

Thanks @Sloth, you code is very useful. This is a slight modification those who are getting an "Object reference not set to an instance of an object." error. It creates an instance of the object is null and also handles arrays. More has to be done to handle all possible collection types but this is a start.

public static IEnumerable<string> GetPropertInfos(object o, string parent = null)
    {


        Type t = o.GetType();
        //   String namespaceValue = t.Namespace;


        PropertyInfo[] props = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);
        foreach (PropertyInfo prp in props)
        {
            if (prp.PropertyType.Module.ScopeName != "CommonLanguageRuntimeLibrary")
            {
                // fix me: you have to pass parent + "." + t.Name instead of t.Name if parent != null
                object value = prp.GetValue(o);
                if (value == null)
                {

                    value =
                        Activator.CreateInstance(Type.GetType(
                            (prp.PropertyType).AssemblyQualifiedName.Replace("[]", "")));
                }

                var propertInfos = GetPropertInfos(value, t.Name);
                foreach (var info in propertInfos)
                    yield return info;
            }
            else
            {
                var type = GetTypeName(prp);

                var info = t.Name + "." + prp.Name ;
                if (String.IsNullOrWhiteSpace(parent))
                    yield return info;
                else
                    yield return parent + "." + info;
            }
        }
    }

您可以使用Type.IsValueType属性。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM