Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I see if GenericTypeDefinition implements IEnumerable<>

Tags:

c#

types

generics

I have a method that checks if a type is generic and then checks if the GenericTypeDefinition is of IEnumerable<>.

static Type GetEnumerableType(Type type)  
{  
    if(type.IsGenericType) {  
        var genericTypeDefinition = type.GetGenericTypeDefinition();  
        if (genericTypeDefinition == typeof(IEnumerable<>)) {  
            return type.GetGenericArguments()[0];  
        }  
    }  
    return null;  
}

Works like a charm if it is an IEnumerable. If the the GenericTypeDefinition is IList<> or List<> it doesn't work. I've tried..

typeof(IEnumerable<>).IsAssignableFrom(genericTypeDefinition)

..without success. Of course there must be a better way then chaining else-statements?

like image 392
Kenny Eliasson Avatar asked Feb 23 '10 15:02

Kenny Eliasson


People also ask

What is IEnumerable<>?

IEnumerable is an interface defining a single method GetEnumerator() that returns an IEnumerator interface. It is the base interface for all non-generic collections that can be enumerated. This works for read-only access to a collection that implements that IEnumerable can be used with a foreach statement.

What is IEnumerable string in c#?

IEnumerable in C# is an interface that defines one method, GetEnumerator which returns an IEnumerator interface. This allows readonly access to a collection then a collection that implements IEnumerable can be used with a for-each statement.


2 Answers

You can use GetInterfaces to check if a type implements IEnumerable<> like so

Type type = new List<string>().GetType();

if (type.IsGenericType) 
{
    var genericTypeDefinition = type.GetGenericTypeDefinition();

    if (genericTypeDefinition.GetInterfaces()
                .Any( t => t.IsGenericType && 
                           t.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
    {
        return type.GetGenericArguments()[0];
    }
}
like image 142
Stan R. Avatar answered Nov 08 '22 01:11

Stan R.


I ended diving way deeper into general object handling and found this way more complex than any original assumptions. This is the method I'm now using:

/// <summary>Check whether the specified type is enumerable.</summary>
/// <param name="type">The type.</param>
/// <param name="underlyingType">IEnumerable{int} would be int</param>
/// <param name="excludeString">
///  [OPTIONAL] if set to <c>true</c> [exclude string]. Strings are enumerable as char[]
///  this is likely not something you want. Default is true (string will return false)
/// </param>
/// <returns><c>true</c> supplied type is enumerable otherwise <c>false</c></returns>
public static bool IsEnumerable(this Type type, out Type underlyingType,
                                bool excludeString = true)
{
    underlyingType = null;

    if (type.IsEnum || type.IsPrimitive || type.IsValueType) return false;

    if (excludeString && type == typeof(string)) return false;

    if (type.IsGenericType)
    {
        if (type.IsTypeDefinitionEnumerable() ||
            type.GetInterfaces()
                .Any(t => t.IsSelfEnumerable() || t.IsTypeDefinitionEnumerable()))
        {
            underlyingType = type.GetGenericArguments()[0];
            return true;
        }
    }
    //direct implementations of IEnumerable<T>, inheritance from List<T> etc
    var enumerableOrNull = type.GetInterfaces()
                               .FirstOrDefault(t => t.IsTypeDefinitionEnumerable());
    if (enumerableOrNull == null) return false;

    underlyingType = enumerableOrNull.GetGenericArguments()[0];
    return true;
}

//

private static bool IsSelfEnumerable(this Type type)
{
    bool isDirectly = type == typeof(IEnumerable<>);
    return isDirectly;
}

private static bool IsTypeDefinitionEnumerable(this Type type)
{
    bool isViaInterfaces = type.IsGenericType && 
                           type.GetGenericTypeDefinition().IsSelfEnumerable();
    return isViaInterfaces;
}

This solution is tested:

Install-Package NUnit -Version 2.6.4

Install-Package Shouldly

[Test]
public void List_is_enumerable()
{
    var sut = new List<int>();

    Type underlyingType;
    var result = sut.IsEnumerable(out underlyingType);

    result.ShouldBeTrue();
    underlyingType.ShouldBe(typeof(int));
}

//

[Test]
public void Yield_return_is_enumerable()
{
    var sut = Yielded();

    Type underlyingType;
    var result = sut.IsEnumerable(out underlyingType);

    result.ShouldBeTrue();
    underlyingType.ShouldBe(typeof(int));
}

private IEnumerable<int> Yielded()
{
    for (int i = 0; i < 3; i++)
    {
        yield return i;
    }
}

//

[Test]
public void int_is_not_an_enumerable()
{
    var sut = 5;

    Type underlyingType;
    var result = sut.IsEnumerable(out underlyingType);

    result.ShouldBe(false);
    underlyingType.ShouldBeNull();
}

[Test]
public void object_is_not_an_enumerable()
{
    var sut = new { foo = 1};

    Type underlyingType;
    var result = sut.IsEnumerable(out underlyingType);

    result.ShouldBe(false);
    underlyingType.ShouldBeNull();
}

Held for posterity. This doesn't answer the original question but was clearly useful to members here.

public static bool IsA<T>(this Type type)
{
    return typeof (T).IsAssignableFrom(type);
}
like image 39
Chris Marisic Avatar answered Nov 08 '22 02:11

Chris Marisic