C#带递归的reflection

我正在研究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); } } 

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

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

 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)将无法按您希望的方式工作,因为String例如是一个类,也不是一个原语。 最好使用prp.PropertyType.Module.ScopeName != "CommonLanguageRuntimeLibrary"

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

这是一个完整的例子:

 IEnumerable 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 

首先,除非你真的需要,否则避免使用reflection。 它很慢,很乱,它的边界不可判坏(我喜欢它,但这是另一回事)

如果你想要转储对象的整个内容,我建议将它转移到对象本身,他们应该知道它们的内部状态。 您可以使用对象ToString方法来编写其内部状态,也可以定义用于显示内部状态的接口

 interface IStateDisplay { string GetInnerState(); } 

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

 Console.WriteLine(user.GetInnerState()); 

另一个选择是使用像AutoMapper这样的工具来隐藏你的reflection的复杂性和复杂性,并公开一个很好的API来使用。

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

 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 )也可以非常复杂并包含它们自己的属性和字段,检查没有意义。

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

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

 public static IEnumerable 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属性。