Reputation: 26494
I want to determine if a generic object type ("T") method type parameter is a collection type. I would typically be sending T through as a Generic.List but it could be any collection type as this is used in a helper function.
Would I be best to test if it implements IEnumerable<T>?
If so, what would the code look like?
Update 14:17 GMT+10 Possibly extending on a solution here (however only works for List<T>'s not IEnumerable<T>'s when it should if List derives ?)
T currentObj;
// works if currentObj is List<T>
currentObj.GetType().GetGenericTypeDefinition() == typeof(List<>)
// does not work if currentObj is List<T>
currentObj.GetType().GetGenericTypeDefinition() == typeof(IEnumerable<>)
Upvotes: 27
Views: 44042
Reputation: 2524
For an ICollection of any type (List or HashSet for example):
internal static bool IsCollection(this Type type) => type.GetGenericArguments().Length > 0 && (typeof(ICollection<>).MakeGenericType(type.GetGenericArguments()[0])).IsAssignableFrom(type);
Upvotes: 0
Reputation: 149
If you want to do a check and get true for any list/collection/IEnumerable, but get false for type of string, then
private static bool IsIEnumerable(Type requestType)
{
var isIEnumerable = typeof(IEnumerable).IsAssignableFrom(requestType);
var notString = !typeof(string).IsAssignableFrom(requestType);
return isIEnumerable && notString;
}
Upvotes: 1
Reputation: 1057
For simplicity and code sharing, I usually use this extension method:
public static bool IsGenericList(this object obj)
{
return IsGenericList(obj.GetType());
}
public static bool IsGenericList(this Type type)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
foreach (Type @interface in type.GetInterfaces())
{
if (@interface.IsGenericType)
{
if (@interface.GetGenericTypeDefinition() == typeof(ICollection<>))
{
// if needed, you can also return the type used as generic argument
return true;
}
}
}
return (type.GetInterface("IEnumerable") != null);
}
Upvotes: 1
Reputation: 6502
I love generics. In this method T
must have a public and parameterless constructor which means you can not use IList<object>
for T
. You must use List<object>
public static T IsEnumerable<T>() where T : new() {
if (new T() is IEnumerable) {
}
Upvotes: 0
Reputation: 631
I came across the same issue while attempting to serialize any object to JSON format. Here is what I ended up using:
Type typ = value.GetType();
// Check for array type
if(typeof(IEnumerable).IsAssignableFrom(typ) || typeof(IEnumerable<>).IsAssignableFrom(typ))
{
List<object> list = ((IEnumerable)value).Cast<object>().ToList();
//Serialize as an array with each item in the list...
}
else
{
//Serialize as object or value type...
}
Upvotes: 0
Reputation: 66573
Personally I tend to use a method that I wrote myself, called TryGetInterfaceGenericParameters
, which I posted below. Here is how to use it in your case:
object currentObj = ...; // get the object
Type[] typeArguments;
if (currentObj.GetType().TryGetInterfaceGenericParameters(typeof(IEnumerable<>), out typeArguments))
{
var innerType = typeArguments[0];
// currentObj implements IEnumerable<innerType>
}
else
{
// The type does not implement IEnumerable<T> for any T
}
It is important to note here that you pass in typeof(IEnumerable<>)
, not typeof(IEnumerable)
(which is an entirely different type) and also not typeof(IEnumerable<T>)
for any T
(if you already know the T
, you don’t need this method). Of course this works with any generic interface, e.g. you can use typeof(IDictionary<,>)
as well (but not typeof(IDictionary)
).
/// <summary>
/// Determines whether the current type is or implements the specified generic interface, and determines that
/// interface's generic type parameters.</summary>
/// <param name="type">
/// The current type.</param>
/// <param name="interface">
/// A generic type definition for an interface, e.g. typeof(ICollection<>) or typeof(IDictionary<,>).</param>
/// <param name="typeParameters">
/// Will receive an array containing the generic type parameters of the interface.</param>
/// <returns>
/// True if the current type is or implements the specified generic interface.</returns>
public static bool TryGetInterfaceGenericParameters(this Type type, Type @interface, out Type[] typeParameters)
{
typeParameters = null;
if (type.IsGenericType && type.GetGenericTypeDefinition() == @interface)
{
typeParameters = type.GetGenericArguments();
return true;
}
var implements = type.FindInterfaces((ty, obj) => ty.IsGenericType && ty.GetGenericTypeDefinition() == @interface, null).FirstOrDefault();
if (implements == null)
return false;
typeParameters = implements.GetGenericArguments();
return true;
}
Upvotes: 9
Reputation: 18181
I would test IEnumerable
instead, since a collection type could implement only IEnumerable
, it doesn't have to implement IEnumerable<T>
.
It also depends: what do you mean with collection type? You could have a collection without implementing any of those interfaces.
Upvotes: 3
Reputation: 1133
While I can't be certain what the original poster's intent was, there have been several responses to the effect of casting to IEnumerable for testing. That's fine, but everyone should be aware that string instances pass this test, which may not be something the original author intended. I know I certainly didn't when I went looking for an answer and found this post:
string testString = "Test";
Console.WriteLine(testString as IEnumerable != null); // returns true
I am in the process of trying to write a custom serializer that uses reflection to accomplish certain tasks. As part of a task, I need to determine if a property value is a collection/array/list of items or a single property value. What is particularly annoying is that several Linq expressions actually result in an enumerable type value, but GetType().IsArray returns false for these, and casting them to ICollection returns null as well, but casting them to IEnumerable returns a non-null value.
So...for the time being, I am still seeking a solution that works for all cases.
Upvotes: 2
Reputation: 754515
In order to get the actual type of T at runtime, you can use the typeof(T) expression. From there the normal type comparison operators will do the trick
bool isEnumerable = typeof(IEnumerable<int>).IsAssignableFrom(typeof(T));
Full Code Sample:
static bool Foo<T>()
{
return typeof(IEnumerable<int>).IsAssignableFrom(typeof(T));
}
Foo<List<T>>(); // true
Foo<int>(); // false
Upvotes: 9
Reputation: 43207
This will be the simplest check..
if(Obj is ICollection)
{
//Derived from ICollection
}
else
{
//Not Derived from ICollection
}
Upvotes: 44
Reputation: 103485
Also, remember just because you are using generics, don't forget other basic techniques, in this case, like overloading. I suspect the you are planning something like this:
void SomeFunc<T>(T t)
{
if (IsCollectionCase(t))
DoSomethingForCollections()
else
DoSOmethingElse();
}
This would be far better handled as:
void SomeFunc(IEnumerable t)
{
DoSomethingForCollections()
}
void SomeFunc<T>(T t)
{
DoSomethingElse()
}
Upvotes: 3
Reputation: 15762
You can use Type.GetInterface() with the mangled name.
private bool IsTAnEnumerable<T>(T x)
{
return null != typeof(T).GetInterface("IEnumerable`1");
}
Upvotes: 17