5

我正在研究 Reflection ,但是在进行递归时我被卡住了。

代码 :

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


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

现在我正在打印这些值。

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

我想知道如何找出属性是类还是原语。如果它是一个类,则进行递归。

4

5 回答 5

16

首先,如果要访问类型的属性,请确保使用具有属性的类型:

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

第二,prp.GetType()永远回来PropertyInfo。您正在寻找prp.PropertyType,它将返回属性的类型。

此外,if(!prp.GetType().IsPrimitive && prp.GetType().IsClass)不会按照您想要的方式工作,因为Stringeg 是一个类,也不是一个原语。更好用prp.PropertyType.Module.ScopeName != "CommonLanguageRuntimeLibrary"

最后但同样重要的是,要使用递归,您实际上必须将代码放入方法中。

这是一个完整的例子:

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

像这样使用:

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

它会输出

User.Name: Foo
User.Number: 19
User.Address.Street: MyStreet
User.Address.State: MyState
User.Address.Country: SomeCountry
于 2013-02-04T09:45:44.937 回答
3

首先,除非你真的需要,否则避免使用反射。它很慢,很乱,几乎无法调试(我喜欢它,但那是另一回事)

如果您想转储对象的全部内容,我建议将其转移到对象本身,它们应该知道它们的内部状态。您可以使用 objectsToString方法来编写它们的内部状态,或者您可以定义一个接口来显示内部状态

interface IStateDisplay
{
   string GetInnerState();
}

并让你的对象实现它。然后显示属性的代码将是

Console.WriteLine(user.GetInnerState());

另一种选择是使用像AutoMapper这样的工具,它可以向您隐藏反射的复杂性和复杂性,并公开一个不错的 API 以供使用。

但是,如果您正在学习反射,打印复杂对象的状态是一个很好的练习。朝那个方向的一些指示:

public string Name;

不是属性,是字段,所以type.GetProperties()不会返回。阅读 C# 属性是什么,以及如何使用和定义它们。最小的属性声明是

public string Name {get; set;}

此外,prp.GetType()将返回类型的类型信息PropertyInfo,而不是它包含的属性的类型。在这种情况下,您需要的是prp.PropertyType属性。

接下来,我不认为Type.IsPrimitive支票是你想要的。该属性对于 Boolean、Byte、SByte、Int16、UInt16、Int32、UInt32、Int64、UInt64、IntPtr、UIntPtr、Char、Double 和 Single 类型返回 true,对于其他所有类型返回 false。最重要的是typeof(string).IsPrimitive返回 false。

同样,我认为Type.IsClass支票也不是您想要的。当您使用它时,仅检查属性是值还是引用类型,并且由于值类型 ( struct) 也可能非常复杂并且包含它们自己的属性和字段,因此检查没有意义。

于 2013-02-04T10:04:55.747 回答
0
 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());
}
}
}
于 2013-02-04T09:39:13.533 回答
0

谢谢@Sloth,你的代码非常有用。对于那些获得“对象引用未设置为对象实例”的人来说,这是一个轻微的修改。错误。它创建的对象实例为 null 并且还处理数组。必须做更多的工作来处理所有可能的集合类型,但这是一个开始。

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;
            }
        }
    }
于 2015-10-26T13:37:37.570 回答
-1

您可以使用该Type.IsValueType物业。

于 2013-02-04T09:47:17.310 回答