views:

440

answers:

3
bool IsTypeAGenericList(Type listType)
{
  typeof(IList<>).IsAssignableFrom(listType.GetGenericTypeDefinition())
}

returns false when given typeof(List<int>).

I assume this is because the two type parameters can be different, correct?

+1  A: 

I guess the method doesn't really make sense, because an instance is never of the generic type - it's always constructed with a particular type argument.

In other words, you could never have an "open" variable to assign into, nor a reference to an open instance to use as the value for the assignment.

As you say, you don't know whether the type parameters will be the same - so (for instance) you could define:

class BizarreList<T> : IList<int>

It feels like there should be some way of expressing the relationship though...

Jon Skeet
Can that relationship really be expressed? Like you said, there is never going to be an instance of a generic type, only instances of a closed constructed type. So can `IList<>` ever be assignable from `List<>`? I say no because it can only be true in the event that both types were constructed with a common type argument and as we have already pointed out, there is no type argument to be had. This is a very interesting problem though :)
Andrew Hare
It would be nice to be able to detect that List<T> implements IList<T> for all T, rather than always implementing IList<string>. I suspect that can be done with enough effort, but it would be painful. Basically the reflection APIs don't do a very good job with generics, IMO.
Jon Skeet
See the answer I posted for code that expresses this relationship. There might be edge cases, but I don't see them.
ripper234
@ripper234: I agree that works for this particular case, and it could probably be reasonably easily expanded to other lists. It just feels like it should be catered for somewhat more easily...
Jon Skeet
I aggree. I also hate SO's minimum comment length.
ripper234
+6  A: 

Actually, this works:

public static bool IsGenericList(Type type)
{
  if (!type.IsGenericType)
    return false;
  var genericArguments = type.GetGenericArguments();
  if (genericArguments.Length != 1)
    return false;

  var listType = typeof (IList<>).MakeGenericType(genericArguments);
  return listType.IsAssignableFrom(type);
}
ripper234
This works for this particular case, but will miss things like MyIntList etc. as shown in Jon's answer. I posted another answer below to try to explain the relationship.
Avish
+1  A: 

This really has to do with open constructed types.

When you say:

class List<T> : IList<T>

You're actually saying: my class is called List, it has one type parameter called T, and it implements the interface that is constructed from IList<> using the same T. So the T in the definition part and the T in the "implements" part both refer to the same type parameter -- you declare it before the colon, then you immediately reference it after the colon.

It gets confusing because IList<>'s type parameter is also called T -- but that is a different type parameter entirely. So let's re-declare our concrete class like this:

class List<U> : IList<U>

This is completely equivalent to the above, only now we can say "U" when we refer to the type parameter of List, and T when we refer to the one from IList. They're different types.

Now it gets easier to see why the generic type definition List<U> (which is what you mean when you say typeof(List<>)) does not implement the generifc type definition IList<T> (which is what you mean when you say typeof(IList<>)), but rather it implements the open generic constructed type IList<U> (that is, IList constructed with List's own type paremeter).

So basically, generic type definitions never inherit or implement other generic type definitions -- they usually implement open constructed types using their own type parameters with other generic type definitions.

Ripper234's answer shows how to handle this particular case using Reflection, so I won't repeat it; I just wanted to clarify the relationship between those types, and I hope it came out at least somewhat intelligible.

Avish