当 属性 是另一个 class 的数组时获取 属性 的名称和值?

Get name and value of property when that property is an array of another class?

我了解如何使用反射获取给定 class 的 属性 名称和值。但是,如果 属性 是一个包含 1 个或多个另一个 class 的数组怎么办?

var person = new
{
    Name = "John",
    Characteristic = new[]
    {
        new Characteristic {Name = "Age", Value = "31"},
        new Characteristic {Name = "Height", Value = "6'1""},
    }
};

var properties = person.GetType().GetProperties();
foreach (var property in properties)
{
    Console.WriteLine(property.Name);
    Console.WriteLine(property.GetValue(person));
    if (property.PropertyType.IsArray)
    {
        // Need to get info here! (See below)
    }
}

基本上,我需要得到特定的 属性 是 Characteristic 的数组,然后是属性 NameValue,然后是这些属性的后续值特性。非常感谢!

**编辑:这是我尝试过的,但我无法获得我需要的值...此代码代替了上面的评论

foreach (var prop in property.GetType().GetProperties())
{
    Console.WriteLine(prop.Name);
    Console.WriteLine(prop.GetValue(property));
}

您仍然使用 GetValue() 但对于数组属性,它 returns 将数组转换为 object 因此您需要将其显式转换回 Array

if (property.PropertyType.IsArray)
{
    foreach (var item in (Array)property.GetValue(person))
        Console.WriteLine(item);
}

如果您还需要访问 item 的属性,请对 item 对象重复相同的 属性 scammin 例程:

if (property.PropertyType.IsArray)
{
    foreach (var item in (Array)property.GetValue(person))
    {
        var subProperties = item.GetType().GetProperties();
        foreach (var subProperty in subProperties)
        {
            Console.WriteLine(subProperty.Name);
            Console.WriteLine(subProperty.GetValue(item));
        }
    }
}

更通用的版本处理任意级别的嵌套数组属性:

TraverseProps(person);
// ..................................................
static void TraverseProps(object obj, int level = 0)
{
    string indent = new string(' ', level * 2);
    if (obj == null)
    {
        Console.WriteLine(indent + "<NULL>");
        return;
    }
    var properties = obj.GetType().GetProperties();
    foreach (var property in properties)
    {
        var value = property.GetValue(obj);
        Console.Write(indent + property.Name + ": ");
        if (property.PropertyType.IsArray)
        {
            Console.WriteLine(indent + "[");
            int i = 0;
            foreach (var item in (Array)value)
            {
                Console.WriteLine(indent + "item " + i + ":");
                TraverseProps(item, level + 1);
                i++;
            }
            Console.WriteLine(indent + "]");
        }
        else
        {
            Console.WriteLine(value);
        }
    }
}

N.B。如果对象树中存在引用循环,此函数容易无限递归。

检索数组实例后(正常使用 GetValue),可以使用 LINQ 的 Cast() 方法将其转换为适当类型的 IEnumerable,如下所示:

var characteristics = person.GetType().GetProperty("Characteristic", BindingFlags.Instance | BindingFlags.Public).GetValue(person, null) as System.Array;
foreach (var c in characteristics.Cast<Characteristic>())
{
    Console.WriteLine("{0} = {1}", c.Name, c.Value);
}

如果您没有早期绑定访问 Characteristic class,您也可以使用纯反射:

var characteristics = person.GetType().GetProperty("Characteristic", BindingFlags.Instance | BindingFlags.Public).GetValue(person, null) as System.Array;
foreach (var o in characteristics)
{
    var name = o.GetType().GetProperty("Name", BindingFlags.Public | BindingFlags.Instance).GetValue(o, null);
    var val  = o.GetType().GetProperty("Value", BindingFlags.Public | BindingFlags.Instance).GetValue(o, null);
    Console.WriteLine("{0} = {1}", name, val);
}

两种情况下的输出都是:

Age = 31
Height = 6'1"

Link to DotNetFiddle demo

这是我想出的有效方法。这将在上面的 if 语句中:

if (property.PropertyType.IsArray)
{
    foreach (var item in (Array) property.GetValue(person))
    {
        var props = item.GetType().GetProperties();
        foreach (var prop in props)
        {
            Console.WriteLine(prop.Name);
            Console.WriteLine(prop.GetValue(item));
        }
    }
}