81

Below is some code I use to get the initial state of all public properties in a class for IsDirty checking.

What's the easiest way to see if a property is IEnumerable?

Cheers,
Berryl

  protected virtual Dictionary<string, object> _GetPropertyValues()
    {
        return _getPublicPropertiesWithSetters()
            .ToDictionary(pi => pi.Name, pi => pi.GetValue(this, null));
    }

    private IEnumerable<PropertyInfo> _getPublicPropertiesWithSetters()
    {
        return GetType().GetProperties().Where(pi => pi.CanWrite);
    }

UPDATE

What I wound up doing was adding a few library extensions as follows

    public static bool IsNonStringEnumerable(this PropertyInfo pi) {
        return pi != null && pi.PropertyType.IsNonStringEnumerable();
    }

    public static bool IsNonStringEnumerable(this object instance) {
        return instance != null && instance.GetType().IsNonStringEnumerable();
    }

    public static bool IsNonStringEnumerable(this Type type) {
        if (type == null || type == typeof(string))
            return false;
        return typeof(IEnumerable).IsAssignableFrom(type);
    }
Berryl
  • 12,471
  • 22
  • 98
  • 182

4 Answers4

104
if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType) && prop.PropertyType != typeof(string))
Hans Kesting
  • 38,117
  • 9
  • 79
  • 111
Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172
22

I agree with Fyodor Soikin but the fact that is Enumerable does not mean that is only a Collection since string is also Enumerable and returns the characters one by one...

So i suggest using

if (typeof(ICollection<>).IsAssignableFrom(pi.PropertyType))
sstauross
  • 2,602
  • 2
  • 30
  • 50
  • 1
    You are right about the string of course, but your solution fails (try it with List()). See my update for the code I wound up using. Cheers! – Berryl Mar 16 '13 at 12:18
  • 1
    This fails because no constructed type (like `List`) can be assigned to a generic type ( `ICollection<>`) (in fact, you can't declare a variable of type `ICollection<>`). So better use `typeof(ICollection)` (as suggested by the editor), which will also make it work for non-generic collections. – René Vogt Sep 19 '17 at 12:45
  • Very much indeed – Joanvo Jan 15 '18 at 11:18
8

Try

private bool IsEnumerable(PropertyInfo pi)
{
   return pi.PropertyType.IsSubclassOf(typeof(IEnumerable));
}
Jonas Stensved
  • 14,378
  • 5
  • 51
  • 80
Steve Danner
  • 21,818
  • 7
  • 41
  • 51
  • 3
    I recently noticed that `x.IsSubClassOf(y)` will return false if x == y. In this situation, if the property happened to actually be of type `IEnumerable`, then the function would return false. – Dr. Wily's Apprentice Aug 25 '10 at 21:02
  • 1
    That is interesting, I've honestly never actually used this logic in this exact context, so I'm glad you pointed that out. Thanks. – Steve Danner Aug 26 '10 at 00:33
0

You can also use "pattern matching". This works for both List<T> and IEnumerable<T>.

private void OutputPropertyValues(object obj)
{
    var properties = obj.GetType().GetProperties();

    foreach (var property in properties)
    {
        if (property.GetValue(obj, null) is ICollection items)
        {
            _output.WriteLine($"    {property.Name}:");

            foreach (var item in items)
            {
                _output.WriteLine($"        {item}");
            }
        }
        else
        {
            _output.WriteLine($"    {property.Name}: {property.GetValue(obj, null)}");
        }
    }
}
Derek Foulk
  • 1,892
  • 1
  • 19
  • 37