By Patrick Desjardins


2009-02-17 15:24:36 8 Comments

Let say I have a generic member in a class or method, so:

public class Foo<T>
{
    public List<T> Bar { get; set; }

    public void Baz()
    {
        // get type of T
    }   
}

When I instantiate the class, the T becomes MyTypeObject1, so the class has a generic list property: List<MyTypeObject1>. The same applies to a generic method in a non-generic class:

public class Foo
{
    public void Bar<T>()
    {
        var baz = new List<T>();

        // get type of T
    }
}

I would like to know, what type of objects the list of my class contains. So the list property called Bar or the local variable baz, contains what type of T?

I cannot do Bar[0].GetType(), because the list might contain zero elements. How can I do it?

16 comments

@Ken Smith 2014-08-28 00:07:52

I use this extension method to accomplish something similar:

public static string GetFriendlyTypeName(this Type t)
{
    var typeName = t.Name.StripStartingWith("`");
    var genericArgs = t.GetGenericArguments();
    if (genericArgs.Length > 0)
    {
        typeName += "<";
        foreach (var genericArg in genericArgs)
        {
            typeName += genericArg.GetFriendlyTypeName() + ", ";
        }
        typeName = typeName.TrimEnd(',', ' ') + ">";
    }
    return typeName;
}

public static string StripStartingWith(this string s, string stripAfter)
{
    if (s == null)
    {
        return null;
    }
    var indexOf = s.IndexOf(stripAfter, StringComparison.Ordinal);
    if (indexOf > -1)
    {
        return s.Substring(0, indexOf);
    }
    return s;
}

You use it like this:

[TestMethod]
public void GetFriendlyTypeName_ShouldHandleReallyComplexTypes()
{
    typeof(Dictionary<string, Dictionary<string, object>>).GetFriendlyTypeName()
        .ShouldEqual("Dictionary<String, Dictionary<String, Object>>");
}

This isn't quite what you're looking for, but it's helpful in demonstrating the techniques involved.

@Cedric Arnould 2019-01-09 16:56:41

Hi, thanks for your answer, could you add the extension for "StripStartingWith" too?

@Ken Smith 2019-01-10 03:12:29

@CedricArnould - Added.

@vishal kumar Saxena 2017-01-05 11:45:58

You can use this one for return type of generic list:

public string ListType<T>(T value)
{
    var valueType = value.GetType().GenericTypeArguments[0].FullName;
    return valueType;
}

@Alen.Toma 2017-06-27 21:40:10

This is how i did it

internal static Type GetElementType(this Type type)
{
        //use type.GenericTypeArguments if exist 
        if (type.GenericTypeArguments.Any())
         return type.GenericTypeArguments.First();

         return type.GetRuntimeProperty("Item").PropertyType);
}

Then call it like this

var item = Activator.CreateInstance(iListType.GetElementType());

OR

var item = Activator.CreateInstance(Bar.GetType().GetElementType());

@Tamas Czinege 2009-02-17 15:27:08

If I understand correctly, your list has the same type parameter as the container class itself. If this is the case, then:

Type typeParameterType = typeof(T);

If you are in the lucky situation of having object as a type parameter, see Marc's answer.

@Marc Gravell 2009-02-17 15:32:03

Lol - yes, very true; I assumed that the OP only had object, IList, or similar - but this could very well be the right answer.

@demoncodemonkey 2012-02-17 13:00:45

I love how readable typeof is. If you want to know the type of T, just use typeof(T) :)

@Anton 2017-06-26 14:39:19

I actually just used typeof(Type) and it works great.

@Reynevan 2019-03-08 17:52:19

You can't use typeof() with a generic parameter, though.

@Luaan 2019-07-26 07:41:47

@Reynevan Of course you can use typeof() with a generic parameter. Do you have any example where it wouldn't work? Or are you confusing type parameters and references?

@Fatih Çelik 2016-10-13 11:38:09

If you want to know a property's underlying type, try this:

propInfo.PropertyType.UnderlyingSystemType.GenericTypeArguments[0]

@Sebi 2015-05-08 10:21:15

If you dont need the whole Type variable and just want to check the type you can easily create a temp variable and use is operator.

T checkType = default(T);

if (checkType is MyClass)
{}

@fantastory 2015-06-03 14:41:40

Using 3dGrabber's solution:

public static T GetEnumeratedType<T>(this IEnumerable<T> _)
{
    return default(T);
}

//and now 

var list = new Dictionary<string, int>();
var stronglyTypedVar = list.GetEnumeratedType();

@Karanvir Kang 2015-05-05 18:23:26

public bool IsCollection<T>(T value){
  var valueType = value.GetType();
  return valueType.IsArray() || typeof(IEnumerable<object>).IsAssignableFrom(valueType) || typeof(IEnumerable<T>).IsAssignableFrom(valuetype);
}

@Nathan Tuggy 2015-05-06 00:38:20

This appears to address the question of whether the type is a list-y sort of thing, but the question is more about how to determine what generic type parameter a type that is known to be a List already was initialized with.

@Marc Gravell 2009-02-17 15:26:23

(note: I'm assuming that all you know is object or IList or similar, and that the list could be any type at runtime)

If you know it is a List<T>, then:

Type type = abc.GetType().GetGenericArguments()[0];

Another option is to look at the indexer:

Type type = abc.GetType().GetProperty("Item").PropertyType;

Using new TypeInfo:

using System.Reflection;
// ...
var type = abc.GetType().GetTypeInfo().GenericTypeArguments[0];

@Patrick Desjardins 2009-02-17 15:31:54

Type type = abc.GetType().GetGenericArguments()[0]; ==> Out of bounds array index...

@Marc Gravell 2009-02-17 15:32:41

@Daok : then it isn't a List<T>

@Patrick Desjardins 2009-02-17 15:34:16

Need something for BindingList or List or whatever object that hold a <T>. What I am doing use a custom BindingListView<T>

@Patrick Desjardins 2009-02-17 15:49:01

Give a try with BindingList<T>, our BindingListView<T> inherit from BindingList<T> and both I have try both of your option and it doesn't work. I might do something wrong... but I think this solution work for the type List<T> but not other type of list.

@Patrick Desjardins 2009-02-17 15:57:51

Type type = abc.GetType().GetProperty("Item").PropertyType; return BindingListView<MyObject> instead of MyObject...

@Patrick Desjardins 2009-02-17 20:03:30

I will try to create a code snippet that reproduce that. Might be tonight or tomorrow.

@Michael Stum 2009-12-08 06:32:52

Just a small addition: GetProperty("Item") will throw a System.Reflection.AmbiguousMatchException in the (rare?) case if there is more than one indexer. I handle that case with a try..catch: Try GetProperty, otherwise revert to GetGenericArguments.

@greenoldman 2010-10-19 08:44:49

The second form does not work with obfuscation I guess. Thank you for the anwer anyway, it is exactly what I was looking for.

@Nyerguds 2015-12-02 09:15:59

Peculiar. This just returns a dummy type "T" for me.

@Marc Gravell 2015-12-02 12:52:39

@Nyerguds that usually means you are looking at an "open" generic type, i.e. List<> rather than List<Foo>; or a T from an outer-type.

@Nyerguds 2015-12-03 19:37:40

@MarcGravell Indeed. I got it eventually. I was going through parent classes with a while loop to get the type, since the object's actual class was a child class of the generic type whose internal type I was after. I guess I went one step too far up the hierarchy, and it went from GenericType<ObjectType>, which was the one I needed, to GenericType<T>.

@Ray 2016-11-19 14:33:24

Asking the indexer was required for me in one case: I had a non-generic class inheriting from List<int> and wanted to find the element type int. Since the class itself had no generic arguments, the indexer was the only way to find out.

@3dGrabber 2012-09-23 10:05:30

With the following extension method you can get away without reflection:

public static Type GetListType<T>(this List<T> _)
{
    return typeof(T);
}

Or more general:

public static Type GetEnumeratedType<T>(this IEnumerable<T> _)
{
    return typeof(T);
}

Usage:

List<string>        list    = new List<string> { "a", "b", "c" };
IEnumerable<string> strings = list;
IEnumerable<object> objects = list;

Type listType    = list.GetListType();           // string
Type stringsType = strings.GetEnumeratedType();  // string
Type objectsType = objects.GetEnumeratedType();  // BEWARE: object

@recursive 2015-02-12 20:42:49

This is only useful if you already know the type of T at compile time. In which case, you don't really need any code at all.

@fantastory 2015-06-03 14:42:16

'return default(T);'

@JJJ 2016-06-17 12:15:51

@recursive: It's useful if you're working with a list of an anonymous type.

@toddmo 2016-08-28 17:18:58

I did the exact same thing before I read your answer but I had called mine ItemType

@Dan Malcolm 2014-04-16 15:31:43

You can get the type of "T" from any collection type that implements IEnumerable<T> with the following:

public static Type GetCollectionItemType(Type collectionType)
{
    var types = collectionType.GetInterfaces()
        .Where(x => x.IsGenericType 
            && x.GetGenericTypeDefinition() == typeof(IEnumerable<>))
        .ToArray();
    // Only support collections that implement IEnumerable<T> once.
    return types.Length == 1 ? types[0].GetGenericArguments()[0] : null;
}

Note that it doesn't support collection types that implement IEnumerable<T> twice, e.g.

public class WierdCustomType : IEnumerable<int>, IEnumerable<string> { ... }

I suppose you could return an array of types if you needed to support this...

Also, you might also want to cache the result per collection type if you're doing this a lot (e.g. in a loop).

@Ferenc Mucsi 2010-03-29 19:36:39

Consider this: I use it to export 20 typed list by same way:

private void Generate<T>()
{
    T item = (T)Activator.CreateInstance(typeof(T));

    ((T)item as DemomigrItemList).Initialize();

    Type type = ((T)item as DemomigrItemList).AsEnumerable().FirstOrDefault().GetType();
    if (type == null) return;
    if (type != typeof(account)) //account is listitem in List<account>
    {
        ((T)item as DemomigrItemList).CreateCSV(type);
    }
}

@Nyerguds 2013-07-11 07:48:59

This doesn't work if T is an abstract superclass of the actual added objects. Not to mention, just new T(); would do the same thing as (T)Activator.CreateInstance(typeof(T));. It does require that you add where T : new() to the class/function definition, but if you want to make objects, that should be done anyway.

@Mathias Lykkegaard Lorenzen 2015-03-16 05:56:08

Also, you are calling GetType on a FirstOrDefault entry resulting in a potential null reference exception. If you are sure that it will return at least one item, why not use First instead?

@Thomas 2010-07-29 14:26:04

The GetGenericArgument() method has to be set on the Base Type of your instance (whose class is a generic class myClass<T>). Otherwise, it returns a type[0]

Example:

Myclass<T> instance = new Myclass<T>();
Type[] listTypes = typeof(instance).BaseType.GetGenericArguments();

@Carlos Rodriguez 2011-05-18 10:01:22

That's work for me. Where myList is some unknowed kind of list.

IEnumerable myEnum = myList as IEnumerable;
Type entryType = myEnum.AsQueryable().ElementType;

@Joseph Humfrey 2015-05-21 14:26:08

I get an error that it requires a type argument (i.e. <T>)

@user2334883 2016-01-16 04:58:03

Joseph and others, to get rid of the error it is in System.Collections.

@pipedreambomb 2016-11-22 13:44:30

Just the second line is needed for me. A List is already an implementation of IEnumerable, so the cast doesn't seem to add anything. But thanks, it's a good solution.

@Ferenc Mucsi 2010-03-07 12:12:56

Type:

type = list.AsEnumerable().SingleOrDefault().GetType();

@rossisdead 2010-07-14 02:26:39

This would throw a NullReferenceException if the list has no elements inside it for it to test against.

@devgeezer 2012-03-14 15:08:42

SingleOrDefault() also throws InvalidOperationException when there are two or more elements.

@Oliver 2013-01-23 09:53:25

This answer is wrong, as pointed out correctly by \@rossisdead and \@devgeezer.

@Rauhotz 2009-02-17 15:26:41

Try

list.GetType().GetGenericArguments()

@Rauhotz 2009-02-17 15:40:54

new List<int>().GetType().GetGenericArguments() returns System.Type[1] here with System.Int32 as entry

@GoldBishop 2018-04-18 16:37:10

@Rauhotz the GetGenericArguments method returns an Array object of Type, of which you need to then parse out the position of the Generic Type you need. Such as Type<TKey, TValue>: you would need to GetGenericArguments()[0] to get TKey type and GetGenericArguments()[1] to get TValue type

Related Questions

Sponsored Content

26 Answered Questions

[SOLVED] Why not inherit from List<T>?

27 Answered Questions

[SOLVED] Get int value from enum in C#

  • 2009-06-03 06:46:39
  • jim
  • 1445232 View
  • 1731 Score
  • 27 Answer
  • Tags:   c# enums casting int

7 Answered Questions

[SOLVED] How do I use reflection to call a generic method?

25 Answered Questions

[SOLVED] Get generic type of class at runtime

19 Answered Questions

[SOLVED] How to get a class instance of generics type T

  • 2010-08-09 06:58:45
  • robinmag
  • 650223 View
  • 667 Score
  • 19 Answer
  • Tags:   java generics

39 Answered Questions

21 Answered Questions

[SOLVED] Create Generic method constraining T to an Enum

32 Answered Questions

[SOLVED] How do I generate a random int number?

  • 2010-04-24 23:09:11
  • Rella
  • 2198991 View
  • 1829 Score
  • 32 Answer
  • Tags:   c# random

8 Answered Questions

[SOLVED] Java: how do I get a class literal from a generic type?

13 Answered Questions

[SOLVED] Google Gson - deserialize list<class> object? (generic type)

Sponsored Content