Joe White
Joe White

Reputation: 97696

How do I tell whether a Type implements IList<>?

I want to write a method that uses Reflection to tell whether a given Type implements IList<T>. For example:

IsGenericList(typeof(int))                       // should return false
IsGenericList(typeof(ArrayList))                 // should return false
IsGenericList(typeof(IList<int>))                // should return true
IsGenericList(typeof(List<int>))                 // should return true
IsGenericList(typeof(ObservableCollection<int>)) // should return true

In my usage, I can assume that the type will always be an instantiated generic type (or something that's not generic at all).

Unfortunately, this isn't as easy as it ought to be. The obvious solution:

public bool IsGenericList(Type type)
{
    return typeof(IList<>).IsAssignableFrom(type);
}

doesn't work; it always returns false. Apparently non-instantiated generic types like IList<> don't implement IsAssignableFrom the way I'd expect them to: IList<> is not assignable from List<T>.

I've also tried this:

public bool IsGenericList(Type type)
{
    if (!type.IsGenericType)
        return false;
    var genericTypeDefinition = type.GetGenericTypeDefinition();
    return typeof(List<>).IsAssignableFrom(genericTypeDefinition);
}

I.e., turn type into its non-instantiated generic, like IList<int> -> IList<>, and then try IsAssignableFrom again. That will return true when type is an instantiated IList<T> such as IList<int>, IList<object>, etc. But it returns false for classes that implement IList<T> such as List<int>, ObservableCollection<double>, etc., so apparently IList<> is not assignable from List<>. Again, not what I would expect.

How do I go about writing IsGenericList and making it work as in the above examples?

Upvotes: 21

Views: 13395

Answers (8)

cdiggins
cdiggins

Reputation: 18203

I liked Colonel Panic's answer, but I decided to make the answer a bit more generic so I could reuse it another scenarios:

    public static bool InstanceOfGenericType(this Type self, Type genericType)
    {
        return self.IsGenericType && self.GetGenericTypeDefinition() == genericType;
    }

    public static bool InstanceOfGenericInterface(this Type self, Type ifaceType)
    {
        return self.InstanceOfGenericType(ifaceType) 
            || self.GetInterfaces().Any(i => i.InstanceOfGenericType(ifaceType));
    }

    public static bool IsIList(this Type t)
    {
        return t.InstanceOfGenericInterface(typeof(IList<>));
    }

Upvotes: 1

Colonel Panic
Colonel Panic

Reputation: 137574

I too want to test if a type implements IList<T> for some T. I made the obvious change to Lucero's answer but it caused a subtle bug not present in the original answer. Here's my final edit:

    /// <summary>
    /// Test if a type derives from IList of T, for any T.
    /// </summary>
    public bool TestIfGenericList(Type type)
    {
        if (type == null)
        {
            throw new ArgumentNullException("type");
        }

        var interfaceTest = new Predicate<Type>(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>));

        return interfaceTest(type) || type.GetInterfaces().Any(i => interfaceTest(i));
    }

Upvotes: 18

Lucero
Lucero

Reputation: 60190

In fact, you cannot have an instance of a generic type definition. Therefore, the IsAssignableFrom() method works as expected. To achieve what you want, do the following:

public bool IsGenericList(Type type)
{
    if (type == null) {
        throw new ArgumentNullException("type");
    }
    foreach (Type @interface in type.GetInterfaces()) {
        if (@interface.IsGenericType) {
            if (@interface.GetGenericTypeDefinition() == typeof(ICollection<>)) {
                // if needed, you can also return the type used as generic argument
                return true;
            }
        }
    }
    return false;
}

Just out of curiosity, what do you need this for?

Upvotes: 32

tanascius
tanascius

Reputation: 53944

This passes your tests ...

public static bool IsGenericList( Type type )
{
  return type.Name == "IList`1" || type.GetInterface( "IList`1" ) != null;
}

Upvotes: 1

Irwin
Irwin

Reputation: 12819

Using this: http://msdn.microsoft.com/en-us/library/system.type.findinterfaces.aspx

I tried this:

 public class Test : IList<string>
 {
//implementation left out...
 }

class Program
    {
        static void Main(string[] args)
        {
            Test t = new Test();
            TypeFilter myFilter = new TypeFilter(MyInterfaceFilter);

            Type type = t.GetType();
            Type[] x = type.FindInterfaces(myFilter, "System.Collections.Generic.IList");
            Console.WriteLine(x.Length);

        }

        public static bool MyInterfaceFilter(Type typeObj, Object criteriaObj)
        {
            if (typeObj.ToString().Contains(criteriaObj.ToString()))
                return true;
            else
                return false;
        }
    }

Upvotes: 2

Noldorin
Noldorin

Reputation: 147290

Lucero/Reed Copsey both have the right solution now. Just to make it more concise, here it is in LINQified form:

var isGenericList = type.GetInterfaces().Any(t => t.IsGenericType && 
    t.GetGenericTypeDefinition() == typeof(IList<>));

Upvotes: 2

Andy
Andy

Reputation: 30418

Have you tried calling Type.GetInterface()? It's not completely clear from the help, but I think that it will search interfaces implemented by base types, in addition to the type itself. If not, you can always loop through Type.BaseType and call GetInterface() again.

Upvotes: 0

i_am_jorf
i_am_jorf

Reputation: 54600

Use the "is" operator:

The is operator is used to check whether the run-time type of an object is compatible with a given type.

Upvotes: -3

Related Questions