可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I was doing something like Recursively Get Properties & Child Properties Of An Object, but I wanted to use reflection recursively to get each properties. And I got the code from Recursively Print the properties.
The problem with the code is: it only goes one level down, I wonder how can you automatically get all the properties using reflection? I just made up the following sample Container code:
public class Container
{
public Bottle MyBottle { get; set; }
public List<Address> Addresses { get; set; }
public Container()
{
Address a = new Address();
a.AddressLine1 = "1 Main St";
a.AddressLine2 = "2 Main St";
Addresses = new List<Address>();
Addresses.Add(a);
MyBottle = new Bottle();
MyBottle.BottleName = "Big bottle";
MyBottle.BottageAge = 2;
}
}
public class Bottle
{
public string BottleName { get; set; }
public int BottageAge { get; set; }
}
public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public List<SpecialFolder> SpecialFolders { get; set; }
public Address()
{
SpecialFolders = new List<SpecialFolder>();
SpecialFolder sf = new SpecialFolder();
sf.TemplateFolder = Environment.SpecialFolder.Templates.ToString();
sf.UserFolder = Environment.SpecialFolder.UserProfile.ToString();
SpecialFolders.Add(sf);
}
}
public class SpecialFolder
{
public string TemplateFolder { get; set; }
public string UserFolder { get; set; }
}
In the Main method:
static void Main(string[] args)
{
Container c = new Container();
PrintProperties(c);
}
public static void PrintProperties(object obj)
{
PrintProperties(obj, 0);
}
public static void PrintProperties(object obj, int indent)
{
if (obj == null) return;
string indentString = new string(' ', indent);
Type objType = obj.GetType();
PropertyInfo[] properties = objType.GetProperties();
foreach (PropertyInfo property in properties)
{
object propValue = property.GetValue(obj, null);
if (property.PropertyType.Assembly == objType.Assembly)
{
Console.WriteLine("{0}{1}:", indentString, property.Name);
PrintProperties(propValue, indent + 2);
}
else
{
Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue);
}
}
}
I am hoping to get:
MyBottle:
BottleName: Big bottle
BottageAge: 2
Addresses:
AddressLine1: 1 Main St
AddressLine2: 2 Main St
SpecialFolders:
TemplateFolder: Templates
UserFolder: UserProfile
The result I get now:
MyBottle:
BottleName: Big bottle
BottageAge: 2
Addresses: System.Collections.Generic.List`1[TreeViewReflectionExample.Address]
Can someone help me with the PrintProperties method? Thank you very much.
回答1:
You have two problems with your code:
- because of condition
if (property.PropertyType.Assembly == objType.Assembly)
you will omit System.Collections
like List<>
- you do not treat differently
propValue
that are collections. Hence it will print List
properties, not its elements properties.
You can change that for example into:
public void PrintProperties(object obj, int indent)
{
if (obj == null) return;
string indentString = new string(' ', indent);
Type objType = obj.GetType();
PropertyInfo[] properties = objType.GetProperties();
foreach (PropertyInfo property in properties)
{
object propValue = property.GetValue(obj, null);
var elems = propValue as IList;
if (elems != null)
{
foreach (var item in elems)
{
PrintProperties(item, indent + 3);
}
}
else
{
// This will not cut-off System.Collections because of the first check
if (property.PropertyType.Assembly == objType.Assembly)
{
Console.WriteLine("{0}{1}:", indentString, property.Name);
PrintProperties(propValue, indent + 2);
}
else
{
Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue);
}
}
}
}
回答2:
You want to handle primitive types and strings separately, and loop over enumerables instead of just taking their ToString() value. So your code could be updated to:
public void PrintProperties(object obj, int indent)
{
if (obj == null) return;
string indentString = new string(' ', indent);
Type objType = obj.GetType();
PropertyInfo[] properties = objType.GetProperties();
foreach (PropertyInfo property in properties)
{
object propValue = property.GetValue(obj, null);
if(property.PropertyType.IsPrimitive || property.PropertyType == typeof(string))
Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue);
else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
{
Console.WriteLine("{0}{1}:", indentString, property.Name);
IEnumerable enumerable = (IEnumerable)propValue;
foreach(object child in enumerable)
PrintProperties(child, indent + 2);
}
else
{
Console.WriteLine("{0}{1}:", indentString, property.Name);
PrintProperties(propValue, indent + 2);
}
}
}
回答3:
It works for all cases except propValue is string[]. You will get the exception "Parameter Count Mismatch" in line:
object propValue = property.GetValue(obj, null);
To fix this issue you can use this code with a little fix:
private void PrintProperties(object obj, int indent)
{
if (obj == null) return;
string indentString = new string(' ', indent);
Type objType = obj.GetType();
PropertyInfo[] properties = objType.GetProperties();
foreach (PropertyInfo property in properties)
{
object propValue = property.GetValue(obj, null);
var elems = propValue as IList;
if ((elems != null) && !(elems is string[]) )
{
foreach (var item in elems)
{
PrintProperties(item, indent + 3);
}
}
else
{
// This will not cut-off System.Collections because of the first check
if (property.PropertyType.Assembly == objType.Assembly)
{
LogToWindow(String.Format("{0}{1}:", indentString, property.Name));
PrintProperties(propValue, indent + 2);
}
else
{
if (propValue is string[])
{
var str = new StringBuilder();
foreach (string item in (string[])propValue)
{
str.AppendFormat("{0}; ", item);
}
propValue = str.ToString();
str.Clear();
}
LogToWindow(String.Format("{0}{1}: {2}", indentString, property.Name, propValue));
}
}
}
}
回答4:
I changed driis's code bellow. It works for me.
public void PrintProperties(object obj, int indent)
{
if (obj == null)
{
return;
}
string indentString = new string(' ', indent);
Type objType = obj.GetType();
PropertyInfo[] properties = objType.GetProperties();
foreach (PropertyInfo property in properties)
{
object propValue = property.GetValue(obj, null);
if (IsSimpleType(property.PropertyType))
{
Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue);
}
else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
{
if (property.PropertyType == typeof(string[]))
{
Console.WriteLine("{0}{1}: {2}", indentString, property.Name, string.Join(",", (string[])propValue));
}
else
{
Console.WriteLine("{0}{1}:", indentString, property.Name);
IEnumerable enumerable = (IEnumerable)propValue;
foreach (object child in enumerable)
{
PrintProperties(child, indent + 2);
}
}
}
else
{
Console.WriteLine("{0}{1}:", indentString, property.Name);
PrintProperties(propValue, indent + 2);
}
}
}
public static bool IsSimpleType(Type type)
{
return
type.IsValueType ||
type.IsPrimitive ||
new Type[]
{
typeof(String),
typeof(Decimal),
typeof(DateTime),
typeof(DateTimeOffset),
typeof(TimeSpan),
typeof(Guid)
}.Contains(type) ||
Convert.GetTypeCode(type) != TypeCode.Object;
}