Patrick Desjardins
Patrick Desjardins

Reputation: 140983

How to get the type of T from a member of a generic class or method

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?

Upvotes: 867

Views: 914165

Answers (18)

Krunal Dangi
Krunal Dangi

Reputation: 59

To determine the type of objects contained in the list without relying on accessing an element (which might not exist), you can use reflection to obtain the type of the generic parameter T. Here's how you can do it:

For the generic class Foo:

public class Foo<T>
{
    public List<T> Bar { get; set; }
    
    public Type GetGenericType()
    {
        return typeof(T);
    }
}

And for the generic method in the non-generic class Foo:

  public class Foo
  {
      public Type GetGenericType<T>()
      {
          return typeof(T);
      }
  }

With these methods, you can retrieve the type of T used to instantiate the class or method:

 var foo = new Foo<MyTypeObject1>();
 Type typeOfTInFoo = foo.GetGenericType();

 // For the generic method
 var foo2 = new Foo();
 Type typeOfTInBar = foo2.GetGenericType<MyTypeObject1>();

Now, typeOfTInFoo and typeOfTInBar will hold the type MyTypeObject1, representing the type of objects contained in the List property or local variable.

Upvotes: 1

Daniel Ibanga
Daniel Ibanga

Reputation: 59

try this.

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

Upvotes: -1

Alen.Toma
Alen.Toma

Reputation: 4870

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());

Upvotes: -1

vishal kumar Saxena
vishal kumar Saxena

Reputation: 181

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;
}

Upvotes: 15

Fantastory
Fantastory

Reputation: 1984

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();

Upvotes: 1

Sebi
Sebi

Reputation: 3979

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)
{}

Upvotes: 29

Carlos Rodriguez
Carlos Rodriguez

Reputation: 179

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

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

Upvotes: 17

Thomas
Thomas

Reputation: 91

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();

Upvotes: 9

Ferenc Mucsi
Ferenc Mucsi

Reputation: 71

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);
    }
}

Upvotes: 8

Ken Smith
Ken Smith

Reputation: 20445

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.

Upvotes: 8

Tamas Czinege
Tamas Czinege

Reputation: 121404

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.

Upvotes: 908

Fatih &#199;elik
Fatih &#199;elik

Reputation: 541

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

propInfo.PropertyType.UnderlyingSystemType.GenericTypeArguments[0]

Upvotes: 0

Karanvir Kang
Karanvir Kang

Reputation: 2239

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

Upvotes: 0

Marc Gravell
Marc Gravell

Reputation: 1063854

(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];

Upvotes: 592

3dGrabber
3dGrabber

Reputation: 5074

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

Upvotes: 66

Dan Malcolm
Dan Malcolm

Reputation: 4432

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

Upvotes: 6

Ferenc Mucsi
Ferenc Mucsi

Reputation: 71

Type:

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

Upvotes: -9

Rauhotz
Rauhotz

Reputation: 8150

Try

list.GetType().GetGenericArguments()

Upvotes: 34

Related Questions