2

I need a method that returns true if the passed type is assignable to (derives from) the closure of an open (unbounded) generic type. The method should work as follows:

OpenGenericIsAssignableFrom(typeof(ICollection<>), typeof(IList<String>))

Should return true.

Silas Davis
  • 712
  • 1
  • 9
  • 15

2 Answers2

3

Why not simply close the open generic type and see if that is assingable from the closed type? The catch would be that closing it with the same arguments might not be valid so you'd need to catch that

private static bool OpenGenericIsAssignableFrom(
    Type openGenericType, 
    Type typeToCheck)
{
    if (!openGenericType.IsGenericType || typeToCheck == null) return false;

    if(typeToCheck.IsGenericType)
    {
        var typeArgs = typeToCheck.GetGenericArguments();
        if (typeArgs.Length == openGenericType.GetGenericArguments().Length)
        {
            try
            {
              var closed = openGenericType.MakeGenericType(typeArgs);
              return closed.IsAssignableFrom(typeToCheck);
            }
            catch
            {
              //violated type contraints
              return false
            }
        }
    }
    else 
    {
        return OpenGenericIsAssignableFrom(openGenericType, typeToCheck.BaseType) 
              || typeToCheck.GetInterfaces()
                    .Any(i=> OpenGenericIsAssignableFrom(openGenericType,i));
    }
}
Rune FS
  • 21,497
  • 7
  • 62
  • 96
  • Because the type arguments of typeToCheck are only its type arguments (possibly none) and not the type arguments of it's super class. So for example, if `Length : Measurement`, `typeof(length).GetTypeArguments()` is empty so closing `typeof(ICollection<>)` fails. I need to climb the inheritance tree to close `ICollection` since `typeof(ICollection).IsAssignableFrom(typeof(Measurement – Silas Davis Jul 02 '12 at 14:45
  • @silasdavis Fair enough got fooled by your example only using generics to believe you would be passing generics. Updated accordingly – Rune FS Jul 02 '12 at 15:23
  • Ah yes I have posted something similar, but we still need to climb the inheritance tree, take: `IA : A` and `OpenGenericIsAssignableFrom(typeof(IA<>), typeof(A)`. Your version returns false, because it closes `IA` (or fails to) and then stops, but `IA` is assignable from `A` And although it will work I think it is wrong to even try and close the open type unless it is actually of the same generic type as the type to check, since that is what we are really looking for (along with interfaces). – Silas Davis Jul 02 '12 at 16:19
2

Extending Check if a class is derived from a generic class, here is a possible solution:

        private static bool OpenGenericIsAssignableFrom1(Type openGenericType, Type typeToCheck)
        {
            return typeToCheck != null && typeToCheck != typeof(Object) && // Terminate recursion
                ((typeToCheck.IsGenericType && (typeToCheck.GetGenericTypeDefinition() == openGenericType)) || // typeToCheck is as closure of openGenericType
                OpenGenericIsAssignableFrom(openGenericType, typeToCheck.BaseType) || // typeToCheck is the subclass of a closure of openGenericType
                typeToCheck.GetInterfaces().Any(interfaceType => OpenGenericIsAssignableFrom(openGenericType, interfaceType))); // typeToCheck inherits from an interface which is the closure of openGenericType
        }
Community
  • 1
  • 1
Silas Davis
  • 712
  • 1
  • 9
  • 15
  • In which case would `c.IsAssignableFrom(a)` yield a different results from `c.IsAssignableFrom(b)` if a derives from b? – Rune FS Jun 29 '12 at 12:04
  • should return true for both if `c.IsAssignableFrom(b)` is true, and false for both if `c.IsAssignableFrom(a)` is false, given `a : b`. But possible for `c.IsAssignableFrom(b)` to be false when `c.IsAssignableFrom(a)` is true when `c` sits between them or is an interface of `a` but not `b`... – Silas Davis Jul 02 '12 at 16:24