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 i
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;
}
}
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
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 @drzaus for his nice answer, but it can be compressed to a oneliner (plus check for null
s 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))!;