Determine if object derives from collection type

2019-03-15 02:19发布

问题:

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

回答1:

This will be the simplest check..

if(Obj is ICollection)
{
    //Derived from ICollection
}
else
{
    //Not Derived from ICollection
}


回答2:

You can use Type.GetInterface() with the mangled name.

private bool IsTAnEnumerable<T>(T x)
{
    return null != typeof(T).GetInterface("IEnumerable`1");
}


回答3:

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


回答4:

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:

Example of use

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)).

Method source

/// <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&lt;&gt;) or typeof(IDictionary&lt;,&gt;).</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;
}


回答5:

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()
}


回答6:

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.



回答7:

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.



回答8:

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


回答9:

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...
}


回答10:

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) {
}