在C#中使用反射来获取嵌套对象的属性

jhe*_*ngs 69 c# reflection

给出以下对象:

public class Customer {
    public String Name { get; set; }
    public String Address { get; set; }
}

public class Invoice {
    public String ID { get; set; }
    public DateTime Date { get; set; }
    public Customer BillTo { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我想用反射Invoice来获得一个Name属性Customer.这就是我所追求的,假设这段代码可行:

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);
Run Code Online (Sandbox Code Playgroud)

当然,由于"BillTo.Address"不是Invoice该类的有效属性,因此失败.

所以,我尝试编写一个方法将字符串拆分成句点,并遍历对象寻找我感兴趣的最终值.它可以正常工作,但我对它并不完全满意:

public Object GetPropValue(String name, Object obj) {
    foreach (String part in name.Split('.')) {
        if (obj == null) { return null; }

        Type type = obj.GetType();
        PropertyInfo info = type.GetProperty(part);
        if (info == null) { return null; }

        obj = info.GetValue(obj, null);
    }
    return obj;
}
Run Code Online (Sandbox Code Playgroud)

关于如何改进这种方法的任何想法,或解决这个问题的更好方法?

在发布后编辑,我看到了一些相关的帖子......然而,似乎没有专门解决这个问题的答案.另外,我仍然喜欢我的实施反馈.

Dev*_*evT 15

我使用此方法从属性中获取值

"属性"

"街道地址"

"Address.Country.Name"

    public static object GetPropertyValue(object src, string propName)
    {
        if (src == null) throw new ArgumentException("Value cannot be null.", "src");
        if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");

        if(propName.Contains("."))//complex type nested
        {
            var temp = propName.Split(new char[] { '.' }, 2);
            return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]);
        }
        else
        {
            var prop = src.GetType().GetProperty(propName);
            return prop != null ? prop.GetValue(src, null) : null;
        }
    }
Run Code Online (Sandbox Code Playgroud)

这里的小提琴:https://dotnetfiddle.net/PvKRH0


Ree*_*sey 12

我其实认为你的逻辑很好.就个人而言,我可能会更改它,因此您将对象作为第一个参数传递(与PropertyInfo.GetValue更内联,因此不那么令人惊讶).

我也可能会把它称为更像GetNestedPropertyValue的东西,以明确它搜索属性堆栈.


Dom*_*see 11

我知道我参加派对有点晚了,正如其他人所说,你的实施很好
...... 对于简单的用例.
但是,我开发了一个完全解决该用例的库,Pather.CSharp.
它也可作为Nuget Package提供.

它的主要类是Resolver它的Resolve方法.
你传递一个对象,并属性路径,它会返回所需的值.

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
var resolver = new Resolver();
object result = resolver.Resolve(inv, "BillTo.Address");
Run Code Online (Sandbox Code Playgroud)

但它也可以解析更复杂的属性路径,包括数组和字典访问.
因此,例如,如果您Customer多个地址

public class Customer {
    public String Name { get; set; }
    public IEnumerable<String> Addresses { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

你可以使用第二个访问Addresses[1].

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
var resolver = new Resolver();
object result = resolver.Resolve(inv, "BillTo.Addresses[1]");
Run Code Online (Sandbox Code Playgroud)

  • 例如,如何处理嵌套属性(即NullObject.Id)中的空对象,而NullObject在发票上为空? (2认同)

Gab*_*ams 10

您必须访问需要使用反射的ACTUAL对象.这就是我的意思:

而不是这个:

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);
Run Code Online (Sandbox Code Playgroud)

这样做(根据评论编辑):

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo");
Customer cust = (Customer)info.GetValue(inv, null);

PropertyInfo info2 = cust.GetType().GetProperty("Address");
Object val = info2.GetValue(cust, null);
Run Code Online (Sandbox Code Playgroud)

查看这篇文章以获取更多信息: 使用反射来设置对象属性的属性

  • 需要直接获取嵌套属性。我还处于“Invoice”为 T 的情况,并且我有一个路径“Property.Property.Property”的字符串。不能对每个属性都摆弄。 (2认同)

rog*_*r l 7

希望对派对听起来不太晚,我想补充一下我的解决方案:在这种情况下绝对使用递归

public static Object GetPropValue(String name, object obj, Type type)
    {
        var parts = name.Split('.').ToList();
        var currentPart = parts[0];
        PropertyInfo info = type.GetProperty(currentPart);
        if (info == null) { return null; }
        if (name.IndexOf(".") > -1)
        {
            parts.Remove(currentPart);
            return GetPropValue(String.Join(".", parts), info.GetValue(obj, null), info.PropertyType);
        } else
        {
            return info.GetValue(obj, null).ToString();
        }
    }
Run Code Online (Sandbox Code Playgroud)


ito*_*son 6

你没有解释你的"不适"的来源,但你的代码对我来说基本上是合理的.

我唯一要问的是错误处理.如果代码尝试遍历空引用或者属性名称不存在,则返回null.这隐藏了错误:很难知道它是否因为没有BillTo客户而返回null,或者因为你拼错了它"BilTo.Address"......或者因为有一个BillTo客户,并且它的地址为空!在这些情况下,我会让方法崩溃并烧掉 - 只是让异常逃脱(或者将它包装在更友好的一个中).