Reputation: 140983
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
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
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
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
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
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
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
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
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
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
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
Reputation: 541
If you want to know a property's underlying type, try this:
propInfo.PropertyType.UnderlyingSystemType.GenericTypeArguments[0]
Upvotes: 0
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
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
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
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