C# - Get the item type for a generic list

C#CollectionsGenerics

C# Problem Overview


What would be the best way of getting the type of items a generic list contains? It's easy enough to grab the first item in the collection and call .GetType(), but I can't always be sure there will be an item in the collection.

Hope that makes sense.

Thanks,
Sonny

C# Solutions


Solution 1 - C#

You could use the Type.GetGenericArguments method for this purpose.

List<Foo> myList = ...

Type myListElementType = myList.GetType().GetGenericArguments().Single();

Solution 2 - C#

For a more robust approach:

public static Type GetListType(object someList)
{
    if (someList == null)
        throw new ArgumentNullException("someList");

    var type = someList.GetType();

    if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(List<>))
        throw new ArgumentException("Type must be List<>, but was " + type.FullName, "someList");

    return type.GetGenericArguments()[0];
}

But if your variable is typed List<T> then you can just use typeof(T). For example:

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

Note that you don't really even need the someList parameter. This method is just an example for how you could use typeof if you are already in a generic method. You only need to use the reflection approach if you don't have access to the T token (the list is stored in a non-generic-typed variable, such as one typed IList, object, etc.).

Solution 3 - C#

list.GetType().GetGenericArguments()[0]

Solution 4 - C#

Here's another way which works for non-generic collections, too:

static Type GetItemType(Type collectionType)
{
    return collectionType.GetMethod("get_Item").ReturnType;
}

That is, get the return type of foo[x], where foo is of the specified type.

Examples:

// Generic type; prints System.Int32
Console.WriteLine(GetItemType(typeof(List<int>)));

// Non-generic type; prints System.String
Console.WriteLine(GetItemType(typeof(System.Collections.Specialized.StringCollection)));

The GetItemType method above has a couple issues, though:

  • It throws a NullReferenceException if the type has no indexing operator.

  • It throws an AmbiguousMatchException if the type has multiple overloads for the indexing operator (e.g. this[string] and this[int]).

Here is a more refined version:

public static Type GetItemType(this Type collectionType)
{
    var types =
        (from method in collectionType.GetMethods()
         where method.Name == "get_Item"
         select method.ReturnType
        ).Distinct().ToArray();
    if (types.Length == 0)
        return null;
    if (types.Length != 1)
        throw new Exception(string.Format("{0} has multiple item types", collectionType.FullName));
    return types[0];
}

Solution 5 - C#

What about this, its all static (e.g. no instances required), and fast (no loops, no usage of linq), and it is simple :) these work for collections:

    [System.Diagnostics.DebuggerHidden]
    public static Type GetIndexedType(this ICollection poICollection)
    {
        PropertyInfo oPropertyInfo = poICollection == null ? null : poICollection.GetType().GetProperty("Item");
        return oPropertyInfo == null ? null : oPropertyInfo.PropertyType;
    }

    [System.Diagnostics.DebuggerHidden]
    public static Type GetEnumeratedType(this ICollection poICollection)
    {
        PropertyInfo oPropertyInfo = poICollection == null ? null : poICollection.GetType().GetMethod("GetEnumerator").ReturnType.GetProperty("Current");
        return oPropertyInfo == null ? null : oPropertyInfo.PropertyType;
    }

And a few simple unit tests:

        [Test]
        public void GetIndexedType()
        {
            Assert.AreEqual(null, ((ICollection)null).GetIndexedType());
            Assert.AreEqual(typeof(int), (new List<int>()).GetIndexedType());
            Assert.AreEqual(typeof(bool), (new SortedList<string, bool>()).GetIndexedType());
        }

        [Test]
        public void GetEnumeratedType()
        {
            Assert.AreEqual(null, ((ICollection)null).GetEnumeratedType());
            Assert.AreEqual(typeof(int), (new List<int>()).GetEnumeratedType());
            Assert.AreEqual(typeof(KeyValuePair<string, bool>), (new SortedList<string, bool>()).GetEnumeratedType());
        }

Notice the fact that there are two ways to look at this, one type may be returned by the indexer and an other type may be returned by the enumerator. The unit test do show both.

Have fun, Frans.

P.s. For enumerables:

    [System.Diagnostics.DebuggerHidden]
    public static Type GetEnumeratedType(this System.Collections.IEnumerable poIEnumerable)
    {
        PropertyInfo oPropertyInfo = poIEnumerable == null ? null : poIEnumerable.GetType().GetMethod("GetEnumerator").ReturnType.GetProperty("Current");
        return oPropertyInfo == null ? null : oPropertyInfo.PropertyType;
    }

And for enumerator:

    [System.Diagnostics.DebuggerHidden]
    public static Type GetEnumeratedType(this System.Collections.IEnumerator poIEnumerator)
    {
        PropertyInfo oPropertyInfo = poIEnumerator == null ? null : poIEnumerator.GetType().GetProperty("Current");
        return oPropertyInfo == null ? null : oPropertyInfo.PropertyType;
    }

Solution 6 - C#

    public Type GetType(IEnumerable<object> resultList)
    {
        return resultList.GetType().GetElementType();
    }

Solution 7 - C#

Old question new method with dynamic

void Foo(){
   Type type GetTypeT(data as dynamic);
}

private static Type GetTypeT<T>(IEnumerable<T> data)
{
    return typeof(T);
}

Solution 8 - C#

Public Shared Function ListItemType(ListType As System.Type) As System.Type

  If Not ListType.IsGenericType Then
    If ListType.BaseType IsNot Nothing AndAlso ListType.BaseType.IsGenericType Then
      Return ListItemType(ListType.BaseType)
    End If
  Else
    Return ListType.GetGenericArguments.Single
  End If
End Function

Solution 9 - C#

Here is a solution that also works with derived classes.

Because with this class :

  public class SubList : List<int>
  {   }

If you call : subList.GetType().GetGenericArguments().Single()

It will throws a System.InvalidOperationException

With this method it works for derived classes :

public Type GetListItemType<T>(List<T> list)
{
  Type type = list.GetType();
  while (type != typeof(List<T>))
    type = type.BaseType;
  return type.GetGenericArguments().Single();
}


var list = new List<int>();
var subList = new SubList();
Console.WriteLine(GetListItemType(list)); // System.Int32
Console.WriteLine(GetListItemType(subList)); // System.Int32

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSonny BoyView Question on Stackoverflow
Solution 1 - C#AniView Answer on Stackoverflow
Solution 2 - C#cdhowieView Answer on Stackoverflow
Solution 3 - C#user541686View Answer on Stackoverflow
Solution 4 - C#Joey AdamsView Answer on Stackoverflow
Solution 5 - C#FdWView Answer on Stackoverflow
Solution 6 - C#csjoyView Answer on Stackoverflow
Solution 7 - C#Thomas Andreè WangView Answer on Stackoverflow
Solution 8 - C#toddmoView Answer on Stackoverflow
Solution 9 - C#A.BaudouinView Answer on Stackoverflow