简体   繁体   中英

.Net reflection to get nested property with custom attribute

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

eg 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; }

    }

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 eg every member of the List<Neighbor> member will have the MyDBField attribute with the Name property of NeighborRing .

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