Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if object is of non-specific generic type in C#

Say I have the following class:

public class General<T> { }

And I want to find out if an object is of that type. I know I can use reflection to find out whether the object is of that generic type with Type.GetGenericTypeDefinition, but I want to avoid that.

Is it possible to do something like obj is General<T>, or obj.GetType().IsAssignableFrom(typeof(General<T>))?

I'm quite surprised that I couldn't find a similar question, although I may have used wrong keywords in my searches.

like image 772
EZLearner Avatar asked Sep 08 '13 18:09

EZLearner


2 Answers

You can do this:

var obj = new General<int>();
var type = obj.GetType();
var isGeneral = 
(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(General<>)) ||
type.GetBaseTypes().Any(x => x.IsGenericType && 
                             x.GetGenericTypeDefinition() == typeof(General<>));

Where GetBaseTypes is the following extension method:

public static IEnumerable<Type> GetBaseTypes(this Type type)
{
    if (type.BaseType == null) return type.GetInterfaces();

    return new []{type}.Concat(
           Enumerable.Repeat(type.BaseType, 1)
                     .Concat(type.GetInterfaces())
                     .Concat(type.GetInterfaces().SelectMany<Type, Type>(GetBaseTypes))
                     .Concat(type.BaseType.GetBaseTypes()));
}

credits to Slacks answer

like image 185
digEmAll Avatar answered Oct 25 '22 20:10

digEmAll


There are many answers to similar questions, but they all require reflection to walk up the type hierarchy. I suspect there is no better way. If performance is critical, caching the result maybe an option. Here is an example using a ConcurrentDictionary as a simple cache. Then the cost is reduced to a simple type lookup (via GetType) and a ConcurrentDictionary lookup after the cache has been initialized.

using System.Collections.Concurrent;

private static ConcurrentDictionary<Tuple<Type,Type>, bool> cache = new ConcurrentDictionary<Tuple<Type,Type>, bool>();

public static bool IsSubclassOfRawGeneric(this Type toCheck, Type generic) {
    var input = Tuple.Create(toCheck, generic);
    bool isSubclass = cache.GetOrAdd(input, key => IsSubclassOfRawGenericInternal(toCheck, generic));
    return isSubclass;
}

private static bool IsSubclassOfRawGenericInternal(Type toCheck, Type generic) {
    while (toCheck != null && toCheck != typeof(object)) {
        var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
        if (generic == cur) {
            return true;
        }
        toCheck = toCheck.BaseType;
    }
    return false;
}

And you would use it like this:

class I : General<int> { }

object o = new I();
Console.WriteLine(o is General<int>); // true
Console.WriteLine(o.GetType().IsSubclassOfRawGeneric(typeof(General<>))); //true
like image 1
Mike Zboray Avatar answered Oct 25 '22 20:10

Mike Zboray