Testing if object is of generic type in C#

83,949

Solution 1

If you want to check if it's an instance of a generic type:

return list.GetType().IsGenericType;

If you want to check if it's a generic List<T>:

return list.GetType().GetGenericTypeDefinition() == typeof(List<>);

As Jon points out, this checks the exact type equivalence. Returning false doesn't necessarily mean list is List<T> returns false (i.e. the object cannot be assigned to a List<T> variable).

Solution 2

I assume that you don't just want to know if the type is generic, but if an object is an instance of a particular generic type, without knowing the type arguments.

It's not terribly simple, unfortunately. It's not too bad if the generic type is a class (as it is in this case) but it's harder for interfaces. Here's the code for a class:

using System;
using System.Collections.Generic;
using System.Reflection;

class Test
{
    static bool IsInstanceOfGenericType(Type genericType, object instance)
    {
        Type type = instance.GetType();
        while (type != null)
        {
            if (type.IsGenericType &&
                type.GetGenericTypeDefinition() == genericType)
            {
                return true;
            }
            type = type.BaseType;
        }
        return false;
    }

    static void Main(string[] args)
    {
        // True
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new List<string>()));
        // False
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new string[0]));
        // True
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new SubList()));
        // True
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new SubList<int>()));
    }

    class SubList : List<string>
    {
    }

    class SubList<T> : List<T>
    {
    }
}

EDIT: As noted in comments, this may work for interfaces:

foreach (var i in type.GetInterfaces())
{
    if (i.IsGenericType && i.GetGenericTypeDefinition() == genericType)
    {
        return true;
    }
}

I have a sneaking suspicion there may be some awkward edge cases around this, but I can't find one it fails for right now.

Solution 3

These are my two favorite extension methods that cover most edge cases of generic type checking:

Works with:

  • Multiple (generic) interfaces
  • Multiple (generic) base classes
  • Has an overload that will 'out' the specific generic type if it returns true (see unit test for samples):

    public static bool IsOfGenericType(this Type typeToCheck, Type genericType)
    {
        Type concreteType;
        return typeToCheck.IsOfGenericType(genericType, out concreteType); 
    }
    
    public static bool IsOfGenericType(this Type typeToCheck, Type genericType, out Type concreteGenericType)
    {
        while (true)
        {
            concreteGenericType = null;
    
            if (genericType == null)
                throw new ArgumentNullException(nameof(genericType));
    
            if (!genericType.IsGenericTypeDefinition)
                throw new ArgumentException("The definition needs to be a GenericTypeDefinition", nameof(genericType));
    
            if (typeToCheck == null || typeToCheck == typeof(object))
                return false;
    
            if (typeToCheck == genericType)
            {
                concreteGenericType = typeToCheck;
                return true;
            }
    
            if ((typeToCheck.IsGenericType ? typeToCheck.GetGenericTypeDefinition() : typeToCheck) == genericType)
            {
                concreteGenericType = typeToCheck;
                return true;
            }
    
            if (genericType.IsInterface)
                foreach (var i in typeToCheck.GetInterfaces())
                    if (i.IsOfGenericType(genericType, out concreteGenericType))
                        return true;
    
            typeToCheck = typeToCheck.BaseType;
        }
    }
    

Here's a test to demonstrate the (basic) functionality:

 [Test]
    public void SimpleGenericInterfaces()
    {
        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>)));
        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>)));

        Type concreteType;
        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>), out concreteType));
        Assert.AreEqual(typeof(IEnumerable<string>), concreteType);

        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>), out concreteType));
        Assert.AreEqual(typeof(IQueryable<string>), concreteType);


    }

Solution 4

You can use shorter code using dynamic althougth this may be slower than pure reflection:

public static class Extension
{
    public static bool IsGenericList(this object o)
    {
       return IsGeneric((dynamic)o);
    }

    public static bool IsGeneric<T>(List<T> o)
    {
       return true;
    }

    public static bool IsGeneric( object o)
    {
        return false;
    }
}



var l = new List<int>();
l.IsGenericList().Should().BeTrue();

var o = new object();
o.IsGenericList().Should().BeFalse();
Share:
83,949

Related videos on Youtube

Richbits
Author by

Richbits

Updated on July 08, 2022

Comments

  • Richbits
    Richbits almost 2 years

    I would like to perform a test if an object is of a generic type. I've tried the following without success:

    public bool Test()
    {
        List<int> list = new List<int>();
        return list.GetType() == typeof(List<>);
    }
    

    What am I doing wrong and how do I perform this test?

  • Jon Skeet
    Jon Skeet almost 15 years
    That won't detect subtypes though. See my answer. It's also much harder for interfaces :(
  • SKJ
    SKJ about 13 years
    It's correct for a different question. For this question, it's incorrect, as it only addresses (significantly less than) half of the problem.
  • SKJ
    SKJ about 13 years
    Just discovered a problem with this. It only goes down a single line of inheritance. If, along the way, you have a base with both a base class and the interface you're looking for, this goes down the class path only.
  • Jon Skeet
    Jon Skeet about 13 years
    @Groxx: True. I've just spotted that I do mention that in the answer though: "It's not too bad if the generic type is a class (as it is in this case) but it's harder for interfaces. Here's the code for a class"
  • yoyo
    yoyo about 11 years
    Stan R's answer does in fact answer the question as posed, but what the OP really meant was "Testing if object is of a particular generic type in C#", for which this answer is indeed incomplete.
  • Admin
    Admin about 11 years
    What if you don't have a way to know <T>? Like, it might be int, or string, but you don't know that. This generates, it would seem, false negatives... so you don't have a T to use, you're just looking through properties of some object and one is a list. How do you know it is a list so you can unpeel it? By this I mean, you don't have a T anywhere nor a type to use. You could guess every type (is it List<int>? is it List<string>?) but what you want to know is IS THIS A A LIST? That question seems hard to answer.
  • Jon Skeet
    Jon Skeet about 11 years
    @RiverC: Yes, you're right - it is fairly hard to answer, for various reasons. If you're only talking about a class, it's not too bad... you can keep walking up the inheritance tree and see whether you hit List<T> in some form or other. If you include interfaces, it's really tricky.
  • Stan R.
    Stan R. almost 11 years
    people are down-voting me because i answered the question in the context of "is a" generic type rather than "is of a" generic type. English is my 2nd languages and such language nuances pass me by often, to my defense the OP did not specifically ask to test against a specific type and in the title asks "is of" generic type...not sure why I deserve downvotes for an ambiguous question.
  • Kevin Cathcart
    Kevin Cathcart over 9 years
    @JonSkeet: Why is it really tricky? My brief testing suggests that adding foreach(var i in type.GetInterfaces()) if(i.IsGenericType && i.GetGenericTypeDefinition()==genericType) return true;, right before the while loop appears to work just fine. Am I overlooking some odd edge case?
  • Jon Skeet
    Jon Skeet over 9 years
    @KevinCathcart: Hmm - you may be right. I seem to remember that there are some really nasty edge cases to consider, but I can't provoke any failures right now. Will edit the post to indicate this.
  • slawekwin
    slawekwin over 7 years
    couldn't you replace the loop in IsInstanceOfGenericType with a call to IsAssignableFrom instead of equality operator (==)?
  • Peter Ivan
    Peter Ivan about 7 years
    Now you know that and you can improve your answer to be more specific and correct.
  • sjb-sjb
    sjb-sjb over 5 years
    See the answer below by Weibe Tijsma that handles interfaces as well.
  • Brendan Weinstein
    Brendan Weinstein over 5 years
    The call to GetGenericTypeDefinition will throw if it's not a generic type. Make sure you check that first.
  • mwryl
    mwryl over 3 years
    @JonSkeet you can detect subtypes using list.GetType().BaseType property.
  • Jon Skeet
    Jon Skeet over 3 years
    @MohammedLarabi: Yes, and that's exactly what my answer does, recursively...