简体   繁体   中英

Reflection, how to get the PropertyType value and convert to IEnumerable

In my solution, I have a business validation service that can be applied on any class with a base class of Entity type.

Now I need to aggregate the rules that are broken but am stuck, I have properties that can be Collections, so i need to check each item in the collection as well.

To do that I have this check

typeof(IEnumerable).IsAssignableFrom(property.PropertyType)

but now that i know that the type is a collection. How do I cast to that Type IEnumerable<T> so that i can proceed with the next step.

This should take as 1st parameter the item from the collection detected.

Something like this

foreach(var collectionItem in collection)
{
      AggregateBrokenRules(collectionItem, ref rules);
}

Where collection is the result of the conversion or cast

private void AggregateBrokenRules(Type reflectedType, ref List<BrokenRule> rules)
{
      /// => don't apply any filter of any kind except for what already is provided 
      PropertyInfo[] properties = reflectedType.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

      /// => iterate through discovered properties
      foreach (PropertyInfo property in properties)
      {
         /// => if type is IEnumerable
         if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
         {
             /// => cast to IEnumerable 
             var propertyVal = Convert.ChangeType(types, property.PropertyType);


             AggregateBrokenRules(property.PropertyType, ref rules);
          }

          /// => only properties that are of type Entity
          if (typeof(Entity).GetTypeInfo().IsAssignableFrom(property.PropertyType))
          {
              /// => check next level
              AggregateBrokenRules(property.PropertyType, ref rules);
          }

           /// => get the value from this property
           object propertyValue = property.GetValue(reflectedType);
        }
}

It generally helps if you write out a specification that describes what you want and then implement functions that handle each part. This can provide a bit more clarity up front or introduce clarity in an existing issue. For example:

A type's properties may be inspected.

IEnumerable<PropertyInfo> GetInspectableProperties(Type type) => type.GetProperties(BindingFlags.Public | BindingFlags.Instance);

A type may be a sequence of Entity instances.

bool TypeCanBeEnumeratedAsEntitySequence(Type type) => typeof(IEnumerable<Entity>).IsAssignableFrom(type);

An instance with an Entity sequence property may have the instances present in the collection retrieved.

IEnumerable<Entity> GetEntitiesFromProperty(object instance, PropertyInfo property) => (IEnumerable<Entity>)property.GetValue(instance);

An instance may be evaluated for instances that have broken the rules.

IEnumerable<BrokenRule> GetBrokenRulesFor(object instance)
{
    var type = instance.GetType();
    var properties = GetInspectableProperties(type);

    foreach (var property in properties)
    {
        if (TypeCanBeEnumeratedAsEntitySequence(property.PropertyType))
        {
            var instanceTypesInCollection = GetEntitiesFromProperty(instance, property);
            var brokenRulesInCollection = instanceTypesInCollection.Select(x => GetBrokenRulesFor(x)).SelectMany(x => x);

            // ...
        }
    }    
}

You may notice that we're talking about instances and not types . Due to your interest in going through collections, it's likely that you don't care whether the structure of a particular type is invalid, you probably care whether you've been given a particular instance that either breaks the rules or contains properties which contain instances that break the rules.

You may want to change your aggregation method accordingly.

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