2

I want to know the best way to get the prop info and value using reflection for a nested class by its custom attribute name. With below code I can get the prop info via recursion. But is there a better way or using LINQ. Note that I do not want to hard code the class type as similar to other solution

I also want to get the property value by custom attribute

e.g var propValue = ?????

 public class PlanetRoot

    {

        public void GetNeighborMoon()

        {

            Planet planet = new Planet();

            Product product = new Product();

            Neighbor neighbor = new Neighbor();

            neighbor.Moons = 10;

            neighbor.RingColor = "Red";

            product.Neighbors = new List<Neighbor>();

            product.Neighbors.Add(neighbor);

            planet.Product = product;



            //1. Get the RingColor property info of neighbor with attribute MyDBField(Name = "NeighborRing") . Is there a better way

            PropertyInfo propInfo = null;
            DoRecursiveGetProperty(planet.GetType(), "NeighborRing", out propInfo );



            //2. Get the RingColor property value of neighbor with attribute MyDBField(Name = "NeighborRing")

            //var propValue = GetPropertyValue(????);

        }

    }



    private static PropertyInfo DoRecursiveGetProperty(Type type, string attribName, out PropertyInfo propInfo)

    {

        PropertyInfo[] pi = type.GetProperties();

        propInfo= null;

        foreach (PropertyInfo p in pi)

        {

            var dbFieldAttribute = (MyDBFieldAttribute)Attribute.GetCustomAttribute(p, typeof(MyDBFieldAttribute));

            if (dbFieldAttribute != null && attribName.ToUpper() == dbFieldAttribute.Name.ToUpper())

            {

                propInfo= p;

                //Console.WriteLine(p.Name + " : " + (dbFieldAttribute != null && dbFieldAttribute.Name != null ? dbFieldAttribute.Name : "****"));

                return true;

            }



            if (p.PropertyType.IsClass && !p.PropertyType.IsValueType && !p.PropertyType.IsPrimitive

            && p.PropertyType.FullName != "System.String")
                if (propInfo != null) return true;
                else DoRecursiveGetProperty(p.PropertyType, attribName, out propInfo);



        }



        return false;



    }

    public class Planet

    {

        public string PlanetId { get; set; }

        public string Name { get; set; }

        public string Description { get; set; }

        public Product Product { get; set; }



        [MyDBField(Name="PubDate")]

        public string Publishdate { get; set; }



    }



    public class Product

    {

        public string ProductId { get; set; }

        public List<Neighbor> Neighbors { get; set; }



    }



    public class Neighbor

    {

        [MyDBField(Name = "NeighborRing")]

        public string RingColor { get; set; }

        public int Moons { get; set; }

    }



    public class MyDBFieldAttribute : System.Attribute

    {

        public string Name { get; set; }

    }
6
  • Your point 2 seems unclear - the attribute "NeighborRing" applies to every instance of Neighbor so every member of the Neighbors list has that attribute - which value do you want? IOTW, you already know Planet.Product.Neighbors[].RingColor is the property in question - why do you need to use Reflection? Commented Apr 22, 2020 at 19:46
  • While I agree with @NetMage, if you just want a property's value while holding a PropertyInfo instance and the object you want the property of, you can just use PropertyInfo.GetValue: object propValue = propInfo.GetValue(planet); under your line with 2.. Commented Apr 22, 2020 at 19:55
  • Do you want to iterate through the properties of a class, and then the properties of each property value, and so forth, and then return just a single PropertyInfo? That's a little confusing. What if that hierarchy contains more than one property with the attribute? (Sorry, I changed my comment after you replied to it. I had initially pointed out that there were no nested classes.) Commented Apr 22, 2020 at 20:04
  • @ScottHannen By nested classes, it is meant classes that contain members that are instances of other classes, e.g. Product is the type of a member of Planet, and hence nested. Commented Apr 22, 2020 at 20:06
  • @SeanSkelly Perhaps the issue is there is no object reference since the PropertyInfo references a nested member of the original class and following that trail isn't obvious? Commented Apr 22, 2020 at 20:13

1 Answer 1

1

In order to get the value for a nested member that might be in a collection, you need to iterate the collections, and track the current object.

Assuming you don't need the resulting PropInfo for other reasons, just try to get the value:

private static bool TryRecursiveGetValueWithMyDBFieldName(object startObject, string attribName, out object propValue) {
    PropertyInfo[] pi = startObject.GetType().GetProperties();

    foreach (PropertyInfo p in pi) {
        var dbFieldAttribute = (MyDBFieldAttribute)Attribute.GetCustomAttribute(p, typeof(MyDBFieldAttribute));
        if (dbFieldAttribute != null && dbFieldAttribute.Name.Equals(attribName, StringComparison.CurrentCultureIgnoreCase)) {
            //Console.WriteLine(p.Name + " : " + (dbFieldAttribute != null && dbFieldAttribute.Name != null ? dbFieldAttribute.Name : "****"));
            propValue = p.GetValue(startObject);
            return true;
        }

        if (p.PropertyType.IsClass && !p.PropertyType.IsValueType && !p.PropertyType.IsPrimitive &&
            !p.PropertyType.FullName.StartsWith("System.")) {
            var tryObject = p.GetValue(startObject);
            if (tryObject != null && TryRecursiveGetValueWithMyDBFieldName(tryObject, attribName, out propValue))
                return true;
        }

        if (p.PropertyType.IsClass && p.GetValue(startObject) is IEnumerable ip) {
            foreach (var obj in ip) {
                if (obj != null && TryRecursiveGetValueWithMyDBFieldName(obj, attribName, out propValue))
                    return true;
            }
        }
    }

    propValue = default;
    return false;
}

To use it, call with the initial object:

var foundAttrib = TryRecursiveGetValueWithMyDBFieldName(planet, "NeighborRing", out var propValue);

NOTE: This will return the value of the first object with a matching attribute, as e.g. every member of the List<Neighbor> member will have the MyDBField attribute with the Name property of NeighborRing.

Sign up to request clarification or add additional context in comments.

4 Comments

I am using .Net 4.7 and when I call var foundAttrib = TryRecursiveGetValueWithMyDBFieldName(planet, "NeighborRing", out var propValue); I get a object reference null exception on line PropertyInfo[] pi = startObject.GetType().GetProperties(); I see that startObject is null when debugging
@SA. I assume you aren't testing with your sample code - I added some null checks for the recursive calls when property values or collection elements are null for some reason.
yes you are right. I was not testing with the sample code. I was using a many level nested object with some property values not set. I added if(startObject != null) and then it works fine. Thank you
@SA. My edited answer prevents recursing when the value is null so you don't make an extra method call - unless your starting call might be with a null reference.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.