我正在做Recursively Get Properties & Child Properties Of An Object之类的事情,但我希望以递归方式使用反射来获取每个属性。我从Recursively Print the properties获得了代码。
代码的问题是:它只降低了一级,我想知道如何使用反射自动获取所有属性?我刚刚编写了以下示例容器代码:
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; }
}
在Main方法中:
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);
}
}
}
我希望得到:
MyBottle:
BottleName: Big bottle
BottageAge: 2
Addresses:
AddressLine1: 1 Main St
AddressLine2: 2 Main St
SpecialFolders:
TemplateFolder: Templates
UserFolder: UserProfile
我现在得到的结果:
MyBottle:
BottleName: Big bottle
BottageAge: 2
Addresses: System.Collections.Generic.List`1[TreeViewReflectionExample.Address]
有人可以帮我使用PrintProperties方法吗?非常感谢你。
答案 0 :(得分:47)
您的代码有两个问题:
if (property.PropertyType.Assembly == objType.Assembly)
,您将省略System.Collections
List<>
propValue
作为集合。因此,它将打印List
属性,而不是其元素属性。 您可以将其更改为:
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);
}
}
}
}
答案 1 :(得分:12)
你想分别处理原始类型和字符串,并循环遍历可枚举而不是仅仅获取它们的ToString()值。因此,您的代码可以更新为:
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);
}
}
}
答案 2 :(得分:1)
除了propValue是string []之外,它适用于所有情况。您将获得行中的“参数计数不匹配”异常: object propValue = property.GetValue(obj,null);
要解决此问题,您可以使用此代码进行一些修复:
<?php
$layer = Mage::getModel("catalog/layer");
$category = Mage::getModel('catalog/category')->load($this->getCategory());
$layer->setCurrentCategory($category);
$attributes = $layer->getFilterableAttributes();
foreach ($attributes as $attribute) {
if ($attribute->getAttributeCode() == 'color') {
$filterBlockName = 'catalog/layer_filter_attribute';
$result = Mage::app()->getLayout()->createBlock($filterBlockName)->setLayer($layer)->setAttributeModel($attribute)->init();
echo '<strong>Color:</strong><br />';
foreach($result->getItems() as $option) {
echo ' <a href="' . $category->getUrl() . '/?color=' . $option->getValue() . '">' . $option->getValue() . ' - ' . $option->getLabel() . '</a><br />';
}
}
}
?>
答案 3 :(得分:1)
基于Konrad Kokosa的回答:
private string ObjectToString(object obj, int indent = 0)
{
if (obj is null)
{
return "";
}
var sb = new StringBuilder();
string indentString = new string(' ', indent);
Type objType = obj.GetType();
foreach (PropertyInfo property in objType.GetProperties())
{
object propValue = property.GetValue(obj);
var elems = propValue as IList;
if (elems != null)
{
foreach (var item in elems)
{
sb.Append($"{indentString}- {property.Name}\n");
sb.Append(ObjectToString(item, indent + 4));
}
}
else if (property.Name != "ExtensionData")
{
sb.Append($"{indentString}- {property.Name}={propValue}\n");
if (property.PropertyType.Assembly == objType.Assembly)
{
sb.Append(ObjectToString(propValue, indent + 4));
}
}
}
return sb.ToString();
}
更新
根据以下较早的问题编辑代码:TargetParameterCountException when enumerating through properties of string
private string ObjectToString(object obj, int indent = 0)
{
var sb = new StringBuilder();
if (obj != null)
{
string indentString = new string(' ', indent);
if (obj is string)
{
sb.Append($"{indentString}- {obj}\n");
}
else if (obj is Array)
{
var elems = obj as IList;
sb.Append($"{indentString}- [{elems.Count}] :\n");
for (int i = 0; i < elems.Count; i++)
{
sb.Append(ObjectToString(elems[i], indent + 4));
}
}
else
{
Type objType = obj.GetType();
PropertyInfo[] props = objType.GetProperties();
foreach (PropertyInfo prop in props)
{
if (prop.GetIndexParameters().Length == 0)
{
object propValue = prop.GetValue(obj);
var elems = propValue as IList;
if (elems != null)
{
foreach (var item in elems)
{
sb.Append($"{indentString}- {prop.Name} :\n");
sb.Append(ObjectToString(item, indent + 4));
}
}
else if (prop.Name != "ExtensionData")
{
sb.Append($"{indentString}- {prop.Name} = {propValue}\n");
if (prop.PropertyType.Assembly == objType.Assembly)
{
sb.Append(ObjectToString(propValue, indent + 4));
}
}
}
else
{
sb.Append($"{indentString}- {prop.Name} ({prop.PropertyType.Name}): <Indexed>\n");
}
}
}
}
return sb.ToString();
}
答案 4 :(得分:1)
我已经花了很多时间来解决这个问题。要正确转储所有属性类型,需要使用许多特定于类型的格式。
我建议您看看github上此文件中的FormatValue方法。这是将属性值写入字符串的主要逻辑: https://github.com/thomasgalliker/ObjectDumper/blob/ada64c7e51fedf57731006959358aa890b5e4344/ObjectDumper/Internal/ObjectDumperCSharp.cs#L98
您还可以将此代码用作nuget包: https://www.nuget.org/packages/ObjectDumper.NET
答案 5 :(得分:-2)
我改变了传言的代码。它对我有用。
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;
}