简体   繁体   English

使用反射从复杂类中获取值

[英]Get Values From Complex Class Using Reflection

I have a class, which is created and populated from an xml string, I've simplified it for example purposes: 我有一个类,它是从xml字符串创建和填充的,我为了示例目的简化了它:

[XmlRoot("Person")]
public sealed class Person
{
    [XmlElement("Name")]
    public string Name { get; set; }

    [XmlElement("Location")]
    public string Location { get; set; }

    [XmlElement("Emails", Type = typeof(PersonEmails)]
    public PersonEmails Emails { get; set; }
}

public class PersonEmails
{
    [XmlElement("Email", Type = typeof(PersonEmail))]
    public PersonEmail[] Emails { get; set; }
}

public class PersonEmail
{
    [XmlAttribute("Type")]
    public string Type { get; set; }

    [XmlText]
    public string Value { get; set; }
}

To extract the information, I'm trying to load them into another class, which is simply: 为了提取信息,我试图将它们加载到另一个类中,这简单地说:

public class TransferObject
{
    public string Name { get; set; }

    public ObjectField[] Fields { get; set; }
}

public class ObjectField
{
    public string Name { get; set; }
    public string Value { get; set; }
}

I'm only populating "Fields" from the other object, which would simply be (Name = "Location", Value = "London"), but for Emails, (Name = "Email"+Type, Value = jeff@here.com) 我只是从另一个对象填充“Fields”,它只是(Name =“Location”,Value =“London”),但对于电子邮件,(Name =“Email”+ Type,Value = jeff @ here。 COM)

Currently I can populate all the other fields, but I'm stuck with Emails, and knowing how to dig deep enough to be able to use reflection (or not) to get the information I need. 目前我可以填充其他所有领域,但我仍然坚持使用电子邮件,并且知道如何深入挖掘以便能够使用反射(或不使用)来获取我需要的信息。 Currently I'm using: 目前我正在使用:

Person person = Person.FromXmlString(xmlString);
List<ObjectField> fields = new List<ObjectField>();
foreach (PropertyInfo pinfo in person.getType().GetProperties()
{
    fields.Add(new ObjectField { Name = pinfo.Name, Value = pinfo.getValue(person, null).ToString();
}

How can I expand on the above to add all my emails to the list? 如何扩展上述内容以将我的所有电子邮件添加到列表中?

You are trying to type cast a complex values type to string value so you lost the data. 您正在尝试将复杂值类型转换为字符串值,以便丢失数据。 Instead use following code: 而是使用以下代码:

class Program
{
    static void Main(string[] args)
    {
        Person person = new Person();
        person.Name = "Person One";
        person.Location = "India";
        person.Emails = new PersonEmails();
        person.Phones = new PersonPhones();
        person.Emails.Emails = new PersonEmail[] { new PersonEmail() { Type = "Official", Value = "xyz@official.com" }, new PersonEmail() { Type = "Personal", Value = "xyz@personal.com" } };
        person.Phones.Phones = new PersonPhone[] { new PersonPhone() { Type = "Official", Value = "789-456-1230" }, new PersonPhone() { Type = "Personal", Value = "123-456-7890" } };

        List<ObjectField> fields = new List<ObjectField>();

        fields = GetPropertyValues(person);

    }

    static List<ObjectField> GetPropertyValues(object obj)
    {
        List<ObjectField> propList = new List<ObjectField>();

        foreach (PropertyInfo pinfo in obj.GetType().GetProperties())
        {
            var value = pinfo.GetValue(obj, null);

            if (pinfo.PropertyType.IsArray)
            {
                var arr = value as object[];
                for (var i = 0; i < arr.Length; i++)
                {
                    if (arr[i].GetType().IsPrimitive)
                    {
                        propList.Add(new ObjectField() { Name = pinfo.Name + i.ToString(), Value = arr[i].ToString() });
                    }
                    else
                    {
                        var lst = GetPropertyValues(arr[i]);
                        if (lst != null && lst.Count > 0)
                            propList.AddRange(lst);
                    }
                }
            }
            else
            {
                if (pinfo.PropertyType.IsPrimitive || value.GetType() == typeof(string))
                {
                    propList.Add(new ObjectField() { Name = pinfo.Name, Value = value.ToString() });
                }
                else
                {
                    var lst = GetPropertyValues(value);
                    if (lst != null && lst.Count > 0)
                        propList.AddRange(lst);
                }
            }
        }
        return propList;
    }
}

Check this snippet out: 检查此代码段:

if(pinfo.PropertyType.IsArray)
{
  // Grab the actual instance of the array.
  // We'll have to use it in a few spots.
  var array = pinfo.GetValue(personObject);

  // Get the length of the array and build an indexArray.
  int length = (int)pinfo.PropertyType.GetProperty("Length").GetValue(array);

  // Get the "GetValue" method so we can extact the array values
  var getValue = findGetValue(pinfo.PropertyType);

  // Cycle through each index and use our "getValue" to fetch the value from the array.
  for(int i=0; i<length; i++)
    fields.Add(new ObjectField { Name = pinfo.Name, Value = getValue.Invoke(array, new object[]{i}).ToString();
}

// Looks for the "GetValue(int index)" MethodInfo.
private static System.Reflection.MethodInfo findGetValue(Type t)
{
  return (from mi in t.GetMethods()
    where mi.Name == "GetValue"
    let parms = mi.GetParameters()
    where parms.Length == 1
    from p in parms
    where p.ParameterType == typeof(int)
    select mi).First();
}

You can definately do it with Reflection ... You can take advantage of the fact that a Type can tell you if it's an array or not ( IsArray )... and then take advantage of the fact that an Array has a method GetValue(int index) that will give you a value back. 您可以使用Reflection它...您可以利用Type可以告诉您它是否是数组( IsArray )的事实...然后利用Array具有方法GetValue(int index)的事实GetValue(int index)将返回给你一个值。

Per your comment 根据你的评论

Because Emails is a property within a different class, recursion should be used. 因为Emails是不同类中的属性,所以应该使用递归。 However the trick is knowing when to go to the next level. 然而,诀窍是知道何时进入下一个级别。 Really that is up to you, but if it were me, I would use some sort of Attribute : 真的,这取决于你,但如果是我,我会使用某种Attribute

static void fetchProperties(Object instance, List<ObjectField> fields)
{
  foreach(var pinfo in instance.GetType().GetProperties())
  {
    if(pinfo.PropertyType.IsArray)
    {
      ... // Code described above
    }
    else if(pinfo.PropertyType.GetCustomAttributes(typeof(SomeAttribute), false).Any())
      // Go the next level
      fetchProperties(pinfo.GetValue(instance), fields);
    else
    {
      ... // Do normal code
    }

  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM