Fun with yield, generics, and foreach


We all know that you can use C#’s foreach keyword to iterate through all items in an enumeration. Suppose you have a list of mixed-types:


            object[] list = new object[] {1,2, “abc”, 5f, “def” };


Trying to enumerate through the strings like this would throw an InvalidCastException:
           
foreach (string s in list)
            {
               
Console.WriteLine(s);
            }


That’s because foreach iterates through every element in the enumeration, and tries to cast each to a String.  It does not just iterate through the types that match string.  I assume that language choice was made so that C# doesn’t need an extra type-check in every for-each.


If you just wanted strings, you could filter them out like so:
            foreach (Object o in list)
            {
               
string s = o as string;
               
if (s == null) continue;
               
Console.WriteLine(s);
            }


That’s probably the most efficient way to do it. Just for kicks, in C# 2.0 with generics, you could also write it like:


            foreach (String s in Filter<string, object>(list))
            {
               
Console.WriteLine(s);
            }


Where Filter takes in an enumeration (list) and returns a enumeration for the subset that matches the filter type (string). In other words, it does: {1,2, “abc”, 5f, “def” } –> {“abc”, “def” };  


The Filter function could be defined as:


        // Yield set of TOut that are in the input enumeration
        static IEnumerable<TOut> Filter<TOut, TIn>(IEnumerable<TIn> list)
           
where TOut : TIn // need constraint to cast TIn –> TOut
        {
           
foreach (TIn o in list)
            {
               
if (o is TOut) yield return (TOut) o;
            }
        }
Note that it requires a generic constraints (the “where” keyword) in order to be able to cast TIn to TOut.


Other uses:
You can have more interesting enumeration functions too:


        static IEnumerable<int> Range(int start, int end)
        {
           
for (int i = start; i < end; i++) yield return i;
        }


And the use that like:


        foreach (int i in Range(5, 8)) { Console.WriteLine(i); }


Or have an enumeration do a more complex calculation like compute the fibonacci series:

        // Compute fibonacci series up to Max (> 1).
static IEnumerable<int> Fib(int max)
{
int a = 0;
int b = 1;
yield return 1;

for (int i = 0; i < max – 1; i++)
{
int c = a +b;
yield return c;

a = b;
b = c;
}
}


And then use it like so:


        foreach (int i in Fib(10)) { Console.WriteLine(i); }


Hey, this looks like Linq …:
This is the type of thing that Linq does, but in a much more useful, less contrived way (see Rick’s example around select). If you like this, you ‘re going to love Linq.

Comments (15)

  1. Chris says:

    I would also assume that the reason c# iterates through all objects, and not just the once that match is to allow one to call a sepcific interface on otherwise non-equal object instances.

  2. jmstall says:

    One advantage is that iterating through all the objects means C# doesn’t need to inject an extra check in the foreach, so it lets it do more efficient code-gen.

    "allow one to call a sepcific interface on otherwise non-equal object "

    I don’t understand what you mean here. Can you clarify?

  3. Sean Chase says:

    Cool. 🙂

    You could do this too…

    object[] stuff = new object[] { 1, "asdf", 5f, true, "test", "ing" };

    foreach (string s in new List<object>(stuff).FindAll(delegate(object o) { return (o is string); }))

    {

    Console.WriteLine(s);

    }

  4. Themes says:

    Here you show sweety things about iterators, but this is not enough to call iterators useful.

    C# iterators may be used for REALLY valuable way, to simplify asynchronous execution.

    Here I demonstrate concept of fully asynchronous HTTP server in less than 1000 lines.

    http://msmvps.com/blogs/mihailik/archive/2005/12/26/79813.aspx

  5. abhinaba says:

    I was just going through the example and felt how easy it would be to do this is LINQ.

    object[] list = { 1, 2, "abhinaba", 5f, "basu" };

    foreach( var v in list.Where(x => x is string)){

    Console.WriteLine(v);

    }

    http://blogs.msdn.com/abhinaba/archive/2006/01/31/520348.aspx

  6. Themes says:

    Abhinaba, Console.WriteLine can be used as Action<string>. This makes you code even simpler:

    list.Where(…).ForEach(Console.WriteLine);

  7. jmstall says:

    Those are some nice examples.

  8. abhinaba says:

    Themes thanks for the nice pointer. However, I was not able to find a pre-defined predicate in System.Query so I cooked up the following

    object[] list = { 1, 2, "abhinaba", 5f, "basu" };

    list.Where(x => x is string).ForEach=(Console.WriteLine);

    For ForEach I created my own Extension method

    static class MyExtensions

    {

    public static void ForEach<T>(this IEnumerable<T> list, Action<T> act)

    {

    foreach (T t in list)

    act(t);

    }

    }

  9. Mihailik says:

    Sure, my mistake.

    I saw that ForEach method in Array and List<T>, but it seems ForEach is absent in Linq at this time. Interestingly why…

  10. Mihailik says:

    I’m sorry my name is not Themes. The browser at my work have wrong cookies 🙂

  11. Comparison of AJAX frameworks for ASP.NET

    Extending CodeSmith merge functionality

    Good read on…

  12. Comparison of AJAX frameworks for ASP.NET

    Extending CodeSmith merge functionality

    Good read on SQLCLR…

  13. Comparison of AJAX frameworks for ASP.NET

    Extending CodeSmith merge functionality

    Good read on SQLCLR…