2

I am looking for the most efficient way of iterating through attributes within an object and checking to see if it has a custom decorator. The challenge is that my object has other objects within it that may have this custom decorator and the SUB-Objects may have objects as well with decorators.

For now the code down below is only reaching into the first layer of sub objects is there a particular way in which I can go within the loop N times efficiently?

List<PropertyInfo> allProperties = type.GetProperties().ToList();

Dictionary<string, List<string>> groupIndexes = new Dictionary<string, List<string>>();

foreach (var property in type.GetProperties())
{
    var nestedProperties = property.PropertyType.GetProperties();
    foreach (var nestedProperty in nestedProperties)
    {
        var singleNestedPropertyIndex = nestedProperty.GetCustomAttribute<SingleIndexAttribute>();
        var groupNestedIndex = nestedProperty.GetCustomAttribute<GroupIndexAttribute>();
        var ttlIndex = property.GetCustomAttribute<TTLIndexAttribute>();

        if (singleNestedPropertyIndex != null || groupNestedIndex != null || ttlIndex != null)
        {
            allProperties.Add(nestedProperty);
        }
    }
}
10
  • 10
    Do it in a recursive method. Commented Dec 4, 2017 at 17:22
  • ..or explicitly use Stack<T>. Implementing Breadth-first search or Depth-first search could solve your problem. Both can be done using recursion or stack. Commented Dec 4, 2017 at 17:34
  • @pkuderov That is definitely more helpful, I need to determine WHEN to stop iterating basically when the inner attribubte has no more complex attributes. Commented Dec 4, 2017 at 17:40
  • 3
    Important question: do your objects ever form a cycle? That is, do you ever have a case where A has sub-object B, has sub-object C, has sub-object D, has sub-object A? Because if you do then the algorithm needs to be written cleverly so that it does not go into infinite loops or recursions. Commented Dec 4, 2017 at 17:54
  • 1
    @MongZhu: And there are plenty of value types that refer to reference types. Your claim is simply wrong. Commented Dec 4, 2017 at 18:08

2 Answers 2

1

You could do it non-recursively by keeping a stack of properties yet-to-visit and a hash set of properties already visited. Then, you can do a while loop on the properties yet-to-visit until you've hit them all.

For example (note: code isn't tested):

HashSet<PropertyInfo> visitedProperties = new HashSet<PropertyInfo>();
Stack<PropertyInfo> remainingProperties = new Stack<PropertyInfo>(type.GetProperties());
List<PropertyInfo> foundProperties = new List<PropertyInfo>();

while (remainingProperties.Count > 0)
{
    var currentProperty = remainingProperties.Pop();

    // Process this property if we haven't visited it yet
    // Add returns true if the element is not yet in the set
    if (visitedProperties.Add(currentProperty))
    {

        // Add sub-properties to the remaining property list if we haven't visited them
        var nestedProperties = currentProperty.PropertyType.GetProperties();
        foreach (var nestedProperty in nestedProperties)
        {
            if (!visitedProperties.Contains(nestedProperty))
            {
                remainingProperties.Push(nestedProperty);
            }
        }

        // Check the current property for attributes
        var singleNestedPropertyIndex = nestedProperty.GetCustomAttribute<SingleIndexAttribute>();
        var groupNestedIndex = nestedProperty.GetCustomAttribute<GroupIndexAttribute>();
        var ttlIndex = property.GetCustomAttribute<TTLIndexAttribute>();

        if (singleNestedPropertyIndex != null || groupNestedIndex != null || ttlIndex != null)
        {
            foundProperties.Add(nestedProperty);
        }
    }
}

This will perform in O(N) time, where N is the total number of properties and nested sub-properties in the entire tree.

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

1 Comment

Good. There are many other ways you could improve your algorithm. For example: could you make it a lazily-computed IEnumerable<PropertyInfo> rather than an eagerly-computed List<PropertyInfo>? If you do that then you can use the LINQ sequence operators to build lazily-executed queries.
0

You can perform it by creating recursive method;

    public static void FindSpecialProperties(PropertyInfo property, List<PropertyInfo> allProperties, HashSet<PropertyInfo> recursivedProperties)
    {
        if (recursivedProperties.Contains(property))//Eliminate already recursived property
        {
            return;
        }
        recursivedProperties.Add(property);
        var properties = property.PropertyType.GetProperties();
        if (properties.Length == 0)
        {
            return;
        }
        foreach (var propertyInfo in properties)
        {
            var singleNestedPropertyIndex = propertyInfo.GetCustomAttribute<SingleIndexAttribute>();
            var groupNestedIndex = propertyInfo.GetCustomAttribute<GroupIndexAttribute>();
            var ttlIndex = property.GetCustomAttribute<TTLIndexAttribute>();

            if (singleNestedPropertyIndex != null || groupNestedIndex != null || ttlIndex != null)
            {
                allProperties.Add(propertyInfo);
            }
            allProperties.Add(propertyInfo);
            FindSpecialProperties(propertyInfo, allProperties, recursivedProperties);
        }
    }

Usage

        var recursivedProperties = new HashSet<PropertyInfo>();
        var allProperties = type.GetProperties().ToList();
        foreach (var property in type.GetProperties())
        {
            FindSpecialProperties(property, allProperties, recursivedProperties);
        }

5 Comments

Be careful, with this current code if you have a cycle (Class A has a property of B, B has a property of C, and C has a property of A) you can get in to a infinite loop.
Thank you I will test this as well since I need to do it recursively. I will test it out.
@Rainman Hello friend what is RecursiveTypeProperties referring to? Did you mean to write FindSpecialProperties?
@Scott Chamberlain, you have a point. I updated my answer to prevent infinite loop.
You can't just compare on gethashcode two different objects can have the same hashcode. And a lookup of a list for a contains check is going to be slow, it should be a hashset

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.