简体   繁体   中英

PropertyInfo List is null when using reflection with generics

I am using a method to try and iterate through an object to get all of the object's properties, some of which are lists of objects.

This all works except for the part where I check for objects within the initial object's properties. If it finds a list of objects I want it to iterate through them.

Annoyingly, I'm getting null on my list of whatever type.

I'm now getting an error in VS because pt isn't instantiated, but it would be at run time.

Below is the if statement I'm using to try and catch whatever object/List is being parsed.

Am I going the right (roundabout) way or doing this or is this completely wrong?

Problematic Code - if statement null list:

public static string DeconstructLists<T>(T obj, string body)
        {
            Type type = obj.GetType();
            PropertyInfo[] properties = type.GetProperties();

            foreach (PropertyInfo property in properties)
            {
                if (property.PropertyType == typeof(string) || property.PropertyType == typeof(int) || property.PropertyType == typeof(bool))
                    body += property.Name + " = " + property.GetValue(obj, null) + Environment.NewLine;

                else
                {
                    if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
                    {
                        //null list exception
                        var list = (IEnumerable)property.GetValue(obj, null);
                        foreach (var item in list)
                        {
                            DeconstructLists(item, body);
                        }

                        // stack overflow exception
                        //var props = property.PropertyType.GetGenericArguments()[0];

                        //foreach (var p in props.GetProperties())
                        //{
                        //    DeconstructLists(p, body);
                        //}
                    }
                }
            }
            return body;
        }

Post create method:

public ActionResult Create(Company cmp)
        {
            if (ModelState.IsValid)
            {
                db.companys.Add(cmp);
                db.SaveChanges();
                // send email
                SendEmail(cmp);
                return RedirectToAction("Thankyou", "Home", new { form="ASN" });
            }
            return View(cmp);
        }

Send email method:

public static void SendEmail(Company cm)
        {
            string _body = "";
            string _subject = "ASN Form Request";

            _body = DeconstructLists<Company>(cm, _body);

            using (SmtpClient msgClient = new SmtpClient())
            {
                msgClient.EnableSsl = false;
                msgClient.DeliveryMethod = SmtpDeliveryMethod.Network;
                msgClient.UseDefaultCredentials = false;
                msgClient.Credentials = new NetworkCredential
                    {                    
                        UserName = "",
                        Password = ""
                    };
                msgClient.Host = "";
                msgClient.Port = 0;

                using (MailMessage msg = new MailMessage())
                {
                    msg.To.Add(""); // to add
                    msg.From = new MailAddress("");// from add
                    msg.Subject = _subject;
                    msg.Body = _body;
                    // preparing the message to be sent
                    msgClient.Send(msg);
                }
            }
        }

Class objects:

public class Company
    {
        public int companyId { get; set; }
        public string name { get; set; }
        public string telephone { get; set; }
        public string regNumber { get; set; }
        public virtual IList<Asn> asns { get; set; }
        public virtual IList<Contact> contacts { get; set; }
    }
    public class Contact
    {
        public int contactId { get; set; }
        public int companyId { get; set; }
        public Company company { get; set; }
        public string name { get; set; }
        public string telephone { get; set; }
    }
public class Asn
    {
        public int asnId { get; set; }
        public int companyId { get; set; }
        public Company company { get; set; }
        public bool userBehalf { get; set; }
        public bool something { get; set; }
    }

If it finds a list of objects I want it to iterate through them.

You don't need a list for iteration, the minimum iteratable type is IEnumerable . In fact your if statement is checking just that

if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
{
    // ...
}

So why not just using inside

var list = (IEnumerable)property.GetValue(obj, null);
foreach (var item in list)
{
    DeconstructLists(item, body);
}

EDIT: See this, it works, then see why yours does not:

using System;
using System.Collections;
using System.Collections.Generic;

namespace Tests
{
    class Test
    {
        static void Main(string[] args)
        {
            var company = new Company
            {
                companyId = 1,
                name = "ACME",
            };
            company.asns = new List<Asn>
            {
                new Asn { asnId = 1, companyId = company.companyId, company = company  },
                new Asn { asnId = 2, companyId = company.companyId, company = company  },
            };
            company.contacts = new List<Contact>
            {
                new Contact { contactId = 1, companyId = company.companyId, company = company, name = "Contact1" },
                new Contact { contactId = 2, companyId = company.companyId, company = company, name = "Contact2" }
            };
            var body = DeconstructLists(company, "");
        }
        public static string DeconstructLists<T>(T obj, string body)
        {
            var type = obj.GetType();
            var properties = type.GetProperties();
            foreach (var property in properties)
            {
                if (property.PropertyType == typeof(string) || property.PropertyType == typeof(int) || property.PropertyType == typeof(bool))
                    body += property.Name + " = " + property.GetValue(obj, null) + Environment.NewLine;
                else
                {
                    if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
                    {
                        body = body + property.Name + ": {" + Environment.NewLine;
                        var list = (IEnumerable)property.GetValue(obj, null);
                        foreach (var item in list)
                        {
                            body = body + item.GetType().Name + ": {" + DeconstructLists(item, "") + "}" + Environment.NewLine;
                        }
                        body = body + "}" + Environment.NewLine;
                    }
                }
            }
            return body;
        }
    }
    public class Company
    {
        public int companyId { get; set; }
        public string name { get; set; }
        public string telephone { get; set; }
        public string regNumber { get; set; }
        public virtual IList<Asn> asns { get; set; }
        public virtual IList<Contact> contacts { get; set; }
    }
    public class Contact
    {
        public int contactId { get; set; }
        public int companyId { get; set; }
        public Company company { get; set; }
        public string name { get; set; }
        public string telephone { get; set; }
    }
    public class Asn
    {
        public int asnId { get; set; }
        public int companyId { get; set; }
        public Company company { get; set; }
        public bool userBehalf { get; set; }
        public bool something { get; set; }
    }
}

Maybe you could try something like this in your if statement ?

var genericType = property.PropertyType.GetGenericArguments()[0];
foreach (var prop in genericType.GetProperties())
{
    DeconstructLists(prop, body);
}

This topic may also help you: How to get the type of T from a member of a generic class or method?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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