Checking if Type or instance implements IEnumerable regardless of Type T

35,435

Solution 1

The following line

return (type is IEnumerable);

is asking "if an instance of Type, type is IEnumerable", which clearly it is not.

You want to do is:

return typeof(IEnumerable).IsAssignableFrom(type);

Solution 2

In addition to Type.IsAssignableFrom(Type), you can also use Type.GetInterfaces():

public static bool ImplementsInterface(this Type type, Type interfaceType)
{
    // Deal with the edge case
    if ( type == interfaceType)
        return true;

    bool implemented = type.GetInterfaces().Contains(interfaceType);
    return implemented;
}

That way, if you wanted to check multiple interfaces you could easily modify ImplementsInterface to take multiple interfaces.

Solution 3

To check if some type implements IEnumerable regardless of T one needs to check the GenericTypeDefinition.

public static bool IsIEnumerableOfT(this Type type)
{
    return type.GetInterfaces().Any(x => x.IsGenericType
           && x.GetGenericTypeDefinition() == typeof(IEnumerable<>));
}
Share:
35,435

Related videos on Youtube

Octopoid
Author by

Octopoid

Updated on July 09, 2022

Comments

  • Octopoid
    Octopoid almost 2 years

    I'm doing a heavy bit of reflection in my current project, and I'm trying to provide a few helper methods just to keep everything tidy.

    I'd like to provide a pair of methods to determine if a type or instance implements IEnumerable – regardless of the type T. Here is what I have at the moment:

    public static bool IsEnumerable(this Type type)
    {
        return (type is IEnumerable);
    }
    
    public static bool IsEnumerable(this object obj)
    {
        return (obj as IEnumerable != null);
    }
    

    When I test them using

    Debug.WriteLine("Type IEnumerable:   " + typeof(IEnumerable).IsEnumerable());
    Debug.WriteLine("Type IEnumerable<>: " + typeof(IEnumerable<string>).IsEnumerable());
    Debug.WriteLine("Type List:          " + typeof(List<string>).IsEnumerable());
    Debug.WriteLine("Type string:        " + typeof(string).IsEnumerable());
    Debug.WriteLine("Type DateTime:      " + typeof(DateTime).IsEnumerable());
    Debug.WriteLine("Instance List:      " + new List<string>().IsEnumerable());
    Debug.WriteLine("Instance string:    " + "".IsEnumerable());
    Debug.WriteLine("Instance DateTime:  " + new DateTime().IsEnumerable());
    

    I get this as the result:

    Type IEnumerable:   False
    Type IEnumerable<>: False
    Type List:          False
    Type string:        False
    Type DateTime:      False
    Instance List:      True
    Instance string:    True
    Instance DateTime:  False
    

    The type method doesn't appear to work at all – I had expected a true for the direct System.Collections.IEnumerable match at least.

    I'm aware that string is technically enumerable, albeit with a few caveats. Ideally in this case, however, I'd need the helper method to return false for it. I just need the instances with a defined IEnumerable<T> type to return true.

    I've probably just missed something fairly obvious – can anyone point me in the right direction?

    • Peter Duniho
      Peter Duniho about 9 years
      I don't understand the question. It's clear why typeof() any type doesn't return true; you are asking whether the type object implements the interface, not the type itself. Maybe you want IsAssignableFrom()? But in what way do you think string doesn't qualify? It does have "a defined IEnumerable<T> type".
    • Octopoid
      Octopoid about 9 years
      Yep, that was the issue with the type one - I've been looking at nests of reflection jumping between types and instances all day and got more than a little confused! string does qualify, however in this case I do really need to rule it out - it's probably more of a matter of method naming at this stage. I think I'll just leave this as it is and add another one that just type checks on string first.
    • Jcl
      Jcl about 9 years
      Agree with @JeroenMostert ... the "duplicate" is asking if a type is implementing IEnumerable<x> using reflection, this one is asking if a type is implementing IEnumerable, which is a different thing and requires a different solution (as proven by the different accepted answers)
    • Andie2302
      Andie2302 almost 4 years
      Maybe you look for ICollection<x>instead of IEnumerable<x>
  • Jcl
    Jcl about 9 years
    @Octopoid a string implements IEnumerable<char>, so it's true, because that's what you are asking for
  • dav_i
    dav_i about 9 years
    @Octopoid That's the correct behavior. string is an IEnumerable<char>, which is an IEnumerable.
  • Octopoid
    Octopoid about 9 years
    Yeah, string does qualify, however in this case I do really need to rule it out - it's probably more of a matter of method naming at this stage. I think I'll just leave this as it is and add another one that just type checks on string first.
  • dav_i
    dav_i about 9 years
    @Octopoid Yeah, just check if it's string first.
  • dav_i
    dav_i about 9 years
    @Octopoid Actually it'll be more performant if you check it second!
  • Octopoid
    Octopoid about 9 years
    @dav_i Good point! Very strange, not sure why, it just feels like it would be the other way until you stop and think.. at any rate - checks switched! :)
  • dav_i
    dav_i about 9 years
    @Octopoid I had the same "oh, wait a sec" moment. Hence the double comment :)
  • Jcl
    Jcl about 9 years
    If you are ruling out string for some reason, without seeing your code or knowing the requirements, you probably want to rule out many possible others too (specially if you are making a library that can be used in future code by types unknown at this point). I fail to see why string would be different to many other possible IEnumerables. If that's the case, you'd be better off being explicit about what you support, instead of using "implementing IEnumerable" as a check
  • Wouter
    Wouter over 3 years
    I don't now why this is the accepted answe. IEnumerable is not the same as IEnumerable<T> regardless of T. There can be types that only implement IEnumerable.
  • Jason C
    Jason C almost 3 years
    Small correction: This fails in the edge case where type is itself typeof(IEnumerable<T>), which can give silent failures in certain situations: dotnetfiddle.net/vLFdHW -- the fix is easy though, just throw a .Append(type) right before that .Any so that the type itself is also checked: dotnetfiddle.net/5wj1KE
  • Jason C
    Jason C almost 3 years
    @Wouter It's because checking IEnumerable generally accomplishes most goals in practice. Another thing all the answers here miss is that if the ultimate goal is to see if a type is compatible with foreach, checking for IEnumerable isn't the way to go, since that's not actually what foreach requires: if that's the goal the correct way is to forget about interfaces and explicitly check for the public X GetEnumerator() method, and check that method's return type for a public bool MoveNext() method and a public Y Current property. Then use those to iterate.
  • Wouter
    Wouter almost 3 years
    @JasonC not for my scenario :). Good to know about foreach only needs a method not the interface. Do you know of any examples where this is actually the case?
  • Jason C
    Jason C almost 3 years
    @Wouter You can find them with pastebin.com/S5MkzmXK (I stuck a list of what I found there too, there's not many). Also there's Span<T> except it's a ref struct so you'd never encounter it generically. There's probably some other standard ref structs too that aren't easily findable. See also.
  • Wouter
    Wouter almost 2 years
    but... Why use IsIEnumerableOfT if you already know compile time that it is true?
  • Jason C
    Jason C almost 2 years
    You'd use it if you didn't know at compile time that it was true, e.g. an instance of some arbitrary Type.