5

I have the situation where I am given an object and need to:

  • Determine if that object is a single object or a collection (Array, List, etc)
  • If it is a collection, step though the list.

What I have so far. Testing for IEnumerable does not work. And the conversion to IEnumerable only works for non-primitive types.

static bool IsIEnum<T>(T x)
{
    return null != typeof(T).GetInterface("IEnumerable`1");
}
static void print(object o)
{
    Console.WriteLine(IsIEnum(o));       // Always returns false
    var o2 = (IEnumerable<object>)o;     // Exception on arrays of primitives
    foreach(var i in o2) {
        Console.WriteLine(i);
    }
}
public void Test()
{
    //int [] x = new int[]{1,2,3,4,5,6,7,8,9};
    string [] x = new string[]{"Now", "is", "the", "time..."};
    print(x);       
}

Anyone know how to do this?

001
  • 13,291
  • 5
  • 35
  • 66
  • 3
    If you have generics why on earth are you using object? Why not print(T obj)? Also, have you tried is IEnumerable instead of GetInterface? And for runtime checking you should not be using typeof, you should be using GetType. – It'sNotALie. Feb 20 '13 at 16:22
  • Thanks, all. I used Snippet Compiler to test with and did not notice that it was "using System.Collections.Generic;" by default. I had tried non-generic IEnumerable and got errors until I changed to "using System.Collections". – 001 Feb 20 '13 at 17:26

3 Answers3

10

It is sufficient to check if the object is convertible to the non-generic IEnumerable interface:

var collection = o as IEnumerable;
if (collection != null)
{
    // It's enumerable...
    foreach (var item in collection)
    {
        // Static type of item is System.Object.
        // Runtime type of item can be anything.
        Console.WriteLine(item);
    }
}
else
{
    // It's not enumerable...
}

IEnumerable<T> itself implements IEnumerable and so this will work for generic and non-generic types alike. Using this interface instead of the generic interface avoids issues with generic interface variance: IEnumerable<T> is not necessarily convertible to IEnumerable<object>.

This question discusses generic interface variance in much more detail: Generic Variance in C# 4.0

Community
  • 1
  • 1
Richard Cook
  • 32,523
  • 5
  • 46
  • 71
0

Don't use the generic version of IEnumerable

static void print(object o)
{
    Console.WriteLine(IsIEnum(o));       // Always returns false
    var o2 = o as IEnumerable;     // Exception on arrays of primitives
    if(o2 != null) {
      foreach(var i in o2) {
        Console.WriteLine(i);
      }
    } 
}

You will miss some types that could be used in a foreach if you do it this way. An object that can be used as the collection in a foreach does not need to implement IEnumerable it simply needs to implement GetEnumerator which in turn needs to return a type that has a Currentproperty and a MoveNext method

If the collection is typed and you simply need to support different kinds of collections you could do

static void print<T>(T o) {
    //Not a collection
}

static void print<T>(IEnumerable<T> o) {
   foreach(var i in o2) {
        Console.WriteLine(i);
   }
}

in this case the method overload resolution will pick the right method for you depending on whether or not the object is a collection (in this case defined by implementing IEnumerable<T>)

Rune FS
  • 21,497
  • 7
  • 62
  • 96
0

Use the following code:

Type t = typeof(System.Collections.IEnumerable);

Console.WriteLine(t.IsAssignableFrom(T)); //returns true for collentions
Stefano Altieri
  • 4,550
  • 1
  • 24
  • 41