-1

I have a class with a bunch of properties of type ISet<T>, where T varies from property to property.

I'd like to iterate over an object's properties, find the ones that are some sort of ISet and call Count on each of them.

Thanks.

Michael
  • 1,351
  • 1
  • 11
  • 25
  • 2
    What have you tried so far..? – Sean Jul 24 '19 at 14:41
  • 1
    I guess you should start by [finding an object with the ISet Interface](https://stackoverflow.com/questions/1519530/using-reflection-to-find-interfaces-implemented) – Mong Zhu Jul 24 '19 at 14:49
  • 1
    @MongZhu, I suppose I could check the class *names* for the substring "ISet", but an ISet isn't an ISet, so I don't think I can use *is* or *isAssignableFrom*. – Michael Jul 24 '19 at 15:38
  • Do you want to call the `Count` property on the set, or do you just want to get the number of items in the set? – Sean Jul 24 '19 at 16:06
  • @Sean, I needed the Count property, but I'm curious what you would have suggested had I simply needed the number of items... – Michael Jul 28 '19 at 07:59
  • @Michael - I was just thinking that if you're just after the number of items then you can try and convert the property to an `IEnumerable` and call the `Count()` extension method. – Sean Jul 28 '19 at 14:08

2 Answers2

1

This might work, obj is a target object:

Type targetType = obj.GetType();
foreach (var propertyInfo in targetType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy))
{
    var isetInterface = propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(ISet<>)
        ? propertyInfo.PropertyType
        : null;

    if(isetInterface == null)
    {
        isetInterface = propertyInfo.PropertyType
            .GetInterfaces()
            .FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(ISet<>));
    }

    if (isetInterface != null)
    {
        object isetPropertyValue = propertyInfo.GetValue(obj);

        var countPropertyInfo = isetInterface
            .GetInterfaces()
            .First(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(ICollection<>))
            .GetProperty("Count");

        if (isetPropertyValue == null)
        {
            Console.WriteLine($".{propertyInfo.Name} == null");
        }
        else
        {
            var count = countPropertyInfo.GetValue(isetPropertyValue);
            Console.WriteLine($".{propertyInfo.Name}.Count == {count}");
        }
    }
}
Renat
  • 7,718
  • 2
  • 20
  • 34
  • 1
    It works if the property is something like `HashSet`, but doesn't work if the property is already the interface `ISet`. You checked all the interfaces, but didn't check the property itself. e.g.: it won't handle this class: `public class Foo { public ISet bar {get; set;} }` – Wyck Jul 24 '19 at 15:26
0

@Renat's answer worked for me, but I tweaked it into something I found more convenient:

        public virtual string counts => typeof(Changeset)
        .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy)
        .Where(prop => prop.GetValue(this) != null && count(prop) > 0)
        .Select(prop => $@".{prop.Name}.Count == {count(prop)}")
        .Join("\n");

    private int count(PropertyInfo prop)
    {
        var name = prop.Name;
        var isetInterface = prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(ISet<>)
            ? prop.PropertyType
            : null;

        if (isetInterface == null)
        {
            isetInterface = prop.PropertyType
                .GetInterfaces()
                .FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(ISet<>));
        }

        if (isetInterface != null)
        {
            object isetPropertyValue = prop.GetValue(this);

            var countPropertyInfo = isetInterface
                .GetInterfaces()
                .First(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(ICollection<>))
                .GetProperty("Count");

            if (isetPropertyValue == null)
                return 0;
            else
                return (int) countPropertyInfo.GetValue(isetPropertyValue);
        }

        return 0;
    }

Also, I found that if I can refer to the properties by name I can use

    private string count<T>(Func<Changeset, ISet<T>> selector)
    {
        var count = selector(this).Count;
        return count > 0 ? $"\n{typeof(T).Name}s:\t\t{count}" : "";
    }

as in

$"{count(x => x.prop)}"
Michael
  • 1,351
  • 1
  • 11
  • 25