How do I get the Array Item Type from Array Type in .net

Say I have an System.String[] type object. I can query the type object to determine if it is an array

Type t1 = typeof(System.String[]);
bool isAnArray = t1.IsArray; // should be true

However how do I get a type object of the array item from t1

Type t2 = ....; // should be typeof(System.String)

You can use the instance method Type.GetElementType for this purpose.

Type t2 = t1.GetElementType();

[Returns] the type of the object encompassed or referred to by the current array, pointer, or reference type, or null if the current Type is not an array or a pointer, or is not passed by reference, or represents a generic type or a type parameter in the definition of a generic type or generic method.


Thanks to @psaxton comment pointing out the difference between Array and other collections. As an extension method:

public static class TypeHelperExtensions
{
    /// <summary>
    /// If the given <paramref name="type"/> is an array or some other collection
    /// comprised of 0 or more instances of a "subtype", get that type
    /// </summary>
    /// <param name="type">the source type</param>
    /// <returns></returns>
    public static Type GetEnumeratedType(this Type type)
    {
        // provided by Array
        var elType = type.GetElementType();
        if (null != elType) return elType;

        // otherwise provided by collection
        var elTypes = type.GetGenericArguments();
        if (elTypes.Length > 0) return elTypes[0];

        // otherwise is not an 'enumerated' type
        return null;
    }
}

Usage:

typeof(Foo).GetEnumeratedType(); // null
typeof(Foo[]).GetEnumeratedType(); // Foo
typeof(List<Foo>).GetEnumeratedType(); // Foo
typeof(ICollection<Foo>).GetEnumeratedType(); // Foo
typeof(IEnumerable<Foo>).GetEnumeratedType(); // Foo

// some other oddities
typeof(HashSet<Foo>).GetEnumeratedType(); // Foo
typeof(Queue<Foo>).GetEnumeratedType(); // Foo
typeof(Stack<Foo>).GetEnumeratedType(); // Foo
typeof(Dictionary<int, Foo>).GetEnumeratedType(); // int
typeof(Dictionary<Foo, int>).GetEnumeratedType(); // Foo, seems to work against key

Thanks to @drzaus for his nice answer, but it can be compressed to a oneliner (plus check for nulls and IEnumerable type):

public static Type GetEnumeratedType(this Type type) =>
   type?.GetElementType()
   ?? typeof(IEnumerable).IsAssignableFrom(type)
   ? type.GenericTypeArguments.FirstOrDefault()
   : null;

Added null checkers to avoid exception, maybe I shouldn't (feel free to remove the Null Conditional Operators). Also added a filter so the function only works on collections, not any generic types.

And bear in mind that this could also be fooled by implemented subclasses that change the subject of the collection and the implementor decided to move the collection's generic-type-argument to a later position.


Converted answer for C#8 and nullability:

public static Type GetEnumeratedType(this Type type) => 
        ((type?.GetElementType() ?? (typeof(IEnumerable).IsAssignableFrom(type)
            ? type.GenericTypeArguments.FirstOrDefault()
            : null))!;