55

basically I'm building a very generic T4 template and one of the things I need it to do is say print variable.ToString(). However, I want it to evaluate lists and foreach through them and instead print ListItem.ToString() My T4 template does not know what type variable will be ahead of time, that is why this is so generic.

But my current code that gets generated looks like this:

if(variable!=null)
  if(variable is IEnumerable) //error here
    foreach(var item in variable)
      Write(item.ToString());

I get a compiler error on the marked line for "Using the generic type System.Generic.Collections.IEnumerable requires one type argument"

I don't actually care what type it is though, I just want to know if you can foreach through the variable. What code should I use instead?

Earlz
  • 62,085
  • 98
  • 303
  • 499

9 Answers9

75

You have already accepted an answer however,since generic IEnumerable<T> implements the non generic IEnumerable you can just cast to that.

// Does write handle null? Might need some sanity aswell.

var enumerable = variable as System.Collections.IEnumerable; 

if (enumerable != null)
    foreach(var item in enumerable)
         Write(item);
else
    Write(item);     
  • This looks like it may be the better answer actually. – Earlz Jan 04 '11 at 05:24
  • I changed the answer cause I ended up using this method, which in my opinion is much more clean. – Earlz Jan 04 '11 at 10:35
  • Also, to get around the Mono bug mentioned below, I had to cast `variable` to an `object` first. – Earlz Jan 04 '11 at 10:36
  • @Earlz: How is this any different from your original `if (variable is IEnumerable)` test? If you want to test for `IEnumerable` then that should work fine for you, assuming that you have the right `using` directive at the top of your source. – LukeH Jan 05 '11 at 09:56
  • @Earlz: And if you actually need to test for `IEnumerable` rather than `IEnumerable` then this answer is technically incorrect: all `IEnumerable` implementations will implement `IEnumerable`, but not all `IEnumerable` implementations will implement `IEnumerable`. – LukeH Jan 05 '11 at 09:57
  • @LukeH it is the same - unless `variable` is generic. Which prompts me to make the edit above. –  Jan 06 '11 at 01:11
  • 21
    Keep in mind that String : IEnumerable . This might give some unexpected results. – Arjen de Mooij Apr 10 '16 at 20:10
27

If you want to test for the non-generic IEnumerable then you'll need to include a using System.Collections directive at the top of your source file.

If you want to test for an IEnumerable<T> of some kind then you'll need something like this instead:

if (variable != null)
{
    if (variable.GetType().GetInterfaces().Any(
            i => i.IsGenericType &&
            i.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
    {
        // foreach...
    }
}
LukeH
  • 263,068
  • 57
  • 365
  • 409
  • @Earlz `System.Collections.Generic` != `System.Collections` -- the error in the post is indicative of not finding the non-generic (IEnumerable) type. –  Jan 04 '11 at 03:28
  • Heh, I used this and Mono slapped me with `Invalid IL code in EViewEngine.TestView:BuildOutput (): IL_0105: castclass 0x1b000002` Hmm that's interesting. – Earlz Jan 04 '11 at 03:43
  • The error occurs with the foreach: `foreach(var item in (IEnumerable)v) __Output.Append(item.ToString())` looks like I have a bug to report. – Earlz Jan 04 '11 at 03:45
20

The other answers have pointed out the generic/non-generic IEnumerable difference but I should also point out that you will also want to test for String specifically because it implements IEnumerable but I doubt you'll want to treat it as a collection of characters.

Josh
  • 68,005
  • 14
  • 144
  • 156
10

Since C# 7.0 you can also achieve this so:

if (variable is IEnumerable enumVar)
{
    foreach (var e in enumVar)
    {
        ...
    }
}
Tobi
  • 5,499
  • 3
  • 31
  • 47
8

Well, somewhat simple but... if you only have:

using System.Collections.Generic;

you might need to add:

using System.Collections;

The former defines IEnumerable<T> and latter defines IEnumerable.

Schultz9999
  • 8,717
  • 8
  • 48
  • 87
3

You can actually test the base class of any generic type directly.

instance.GetGenericTypeDefinition()  == typeof(IEnumerable<>)
Rob Deary
  • 977
  • 10
  • 3
3

In general, with no non-generic base type/interface, this requires GetType and a recursive look-up through the base types/interfaces.

However, that doesn't apply here :-) Just use the non-generic IEnumerable (System.Collections.IEnumerable), from which the generic IEnumerable (System.Collections.Generic.IEnumerable<T>) inherits.

2

If you don't care about object type and you are not in Generic method in C# 7.0+

        if (item is IEnumerable<object> enumVar)
        {
            foreach (var e in enumVar)
            {
                    e.ToString();

            }
        }

In C# < 7.0

        if (item is IEnumerable<object>)
        {
            var enumVar = item as IEnumerable<object>;
            foreach (var e in enumVar)
            {
                e.ToString();

            }
            //or you can cast an array to set values, 
            //since IEnumerable won't let you, unless you cast to IList :) 
            //but array version here 
            //https://stackoverflow.com/a/9783253/1818723
        }
Pawel Cioch
  • 2,895
  • 1
  • 30
  • 29
-3

This is an old question, but I wanted to show an alternative method for determining if a SomeType is IEnumerable:

var isEnumerable = (typeof(SomeType).Name == "IEnumerable`1");
  • 1
    This won't work, as any specific type of an enumerable will not look like that. For example, a List would have a name like "List`1". Also, the number will not always be 1, so you can't assume that either. – Jeffrey Harmon Jul 09 '18 at 13:28