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

.NetReflectionTypes

.Net Problem Overview


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)

.Net Solutions


Solution 1 - .Net

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.

Solution 2 - .Net

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

Solution 3 - .Net

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

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
QuestionPreet SanghaView Question on Stackoverflow
Solution 1 - .NetAniView Answer on Stackoverflow
Solution 2 - .NetdrzausView Answer on Stackoverflow
Solution 3 - .NetShimmy WeitzhandlerView Answer on Stackoverflow