832

Let's say I have a generic member in a class or method, like 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 what type of T does the list property called Bar or the local variable baz contain?

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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Patrick Desjardins
  • 136,852
  • 88
  • 292
  • 341

17 Answers17

871

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.

Joshua Duxbury
  • 4,892
  • 4
  • 32
  • 51
Tamas Czinege
  • 118,853
  • 40
  • 150
  • 176
  • 3
    You can't use typeof() with a generic parameter, though. – Reynevan Mar 08 '19 at 17:52
  • 8
    @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? – Luaan Jul 26 '19 at 07:41
580

(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];
Tim Meyer
  • 12,210
  • 8
  • 64
  • 97
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 3
    Type type = abc.GetType().GetGenericArguments()[0]; ==> Out of bounds array index... – Patrick Desjardins Feb 17 '09 at 15:31
  • 34
    @Daok : then it isn't a List – Marc Gravell Feb 17 '09 at 15:32
  • Need something for BindingList or List or whatever object that hold a . What I am doing use a custom BindingListView – Patrick Desjardins Feb 17 '09 at 15:34
  • 1
    Give a try with BindingList, our BindingListView inherit from BindingList 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 but not other type of list. – Patrick Desjardins Feb 17 '09 at 15:49
  • 2
    Type type = abc.GetType().GetProperty("Item").PropertyType; return BindingListView instead of MyObject... – Patrick Desjardins Feb 17 '09 at 15:57
  • I will try to create a code snippet that reproduce that. Might be tonight or tomorrow. – Patrick Desjardins Feb 17 '09 at 20:03
  • 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. – Michael Stum Dec 08 '09 at 06:32
  • The second form does not work with obfuscation I guess. Thank you for the anwer anyway, it is exactly what I was looking for. – greenoldman Oct 19 '10 at 08:44
  • Peculiar. This just returns a dummy type "T" for me. – Nyerguds Dec 02 '15 at 09:15
  • @Nyerguds that usually means you are looking at an "open" generic type, i.e. `List<>` rather than `List`; or a `T` from an outer-type. – Marc Gravell Dec 02 '15 at 12:52
  • @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`, which was the one I needed, to `GenericType`. – Nyerguds Dec 03 '15 at 19:37
  • Asking the indexer was required for me in one case: I had a non-generic class inheriting from `List` 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. – Ray Nov 19 '16 at 14:33
61

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
3dGrabber
  • 4,710
  • 1
  • 34
  • 42
  • 16
    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. – recursive Feb 12 '15 at 20:42
  • 2
    @recursive: It's useful if you're working with a list of an anonymous type. – JJJ Jun 17 '16 at 12:15
34

Try

list.GetType().GetGenericArguments()
sth
  • 222,467
  • 53
  • 283
  • 367
Rauhotz
  • 7,914
  • 6
  • 40
  • 44
  • 7
    new List().GetType().GetGenericArguments() returns System.Type[1] here with System.Int32 as entry – Rauhotz Feb 17 '09 at 15:40
  • 1
    @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`: you would need to `GetGenericArguments()[0]` to get `TKey` type and `GetGenericArguments()[1]` to get `TValue` type – GoldBishop Apr 18 '18 at 16:37
26

If you don’t need the whole Type variable and just want to check the type, you can easily create a temporary variable and use the is operator.

T checkType = default(T);

if (checkType is MyClass)
{}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sebi
  • 3,879
  • 2
  • 35
  • 62
17

The following works for me. Where myList is some unknown kind of list.

IEnumerable myEnum = myList as IEnumerable;
Type entryType = myEnum.AsQueryable().ElementType;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
14

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

public string ListType<T>(T value)
{
    var valueType = value.GetType().GenericTypeArguments[0].FullName;
    return valueType;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
9

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();
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Thomas
  • 91
  • 1
  • 1
8

Consider this:

I use it to export 20 typed lists by the 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);
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ferenc Mucsi
  • 71
  • 1
  • 1
  • 1
    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. – Nyerguds Jul 11 '13 at 07:48
  • 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? – Mathias Lykkegaard Lorenzen Mar 16 '15 at 05:56
8

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.

Ken Smith
  • 20,305
  • 15
  • 100
  • 147
  • An explanation would be in order. E.g., what is the gist of it? What is the idea? Please respond by editing your answer, not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Jun 21 '21 at 20:00
6

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).

Dan Malcolm
  • 4,382
  • 2
  • 33
  • 27
1

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();
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Fantastory
  • 1,891
  • 21
  • 25
0
public bool IsCollection<T>(T value){
  var valueType = value.GetType();
  return valueType.IsArray() || typeof(IEnumerable<object>).IsAssignableFrom(valueType) || typeof(IEnumerable<T>).IsAssignableFrom(valuetype);
}
Karanvir Kang
  • 2,179
  • 19
  • 16
  • 1
    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. – Nathan Tuggy May 06 '15 at 00:38
  • An explanation would be in order. E.g., what is the gist of it? What is the idea? Please respond by [editing your answer](https://stackoverflow.com/posts/30060495/edit), not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Jun 21 '21 at 20:00
0

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

propInfo.PropertyType.UnderlyingSystemType.GenericTypeArguments[0]
Fatih Çelik
  • 401
  • 7
  • 16
-1

This is how I did it:

internal static Type GetElementType(this Type type)
{
    // Use type.GenericTypeArguments if it exists
    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());
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Alen.Toma
  • 4,684
  • 2
  • 14
  • 31
-1

try this.

if (typeof(T) == typeof(Person))

Daniel Ibanga
  • 59
  • 1
  • 9
-9

Type:

type = list.AsEnumerable().SingleOrDefault().GetType();
sth
  • 222,467
  • 53
  • 283
  • 367
Ferenc Mucsi
  • 71
  • 1
  • 1