Jaa


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

  • Anonymous
    January 30, 2006
    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.
  • Anonymous
    January 30, 2006
    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?
  • Anonymous
    January 30, 2006
    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);
    }









  • Anonymous
    January 30, 2006
    The comment has been removed
  • Anonymous
    January 30, 2006
    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
  • Anonymous
    January 30, 2006
    Abhinaba, Console.WriteLine can be used as Action<string>. This makes you code even simpler:

    list.Where(...).ForEach(Console.WriteLine);
  • Anonymous
    January 31, 2006
    Those are some nice examples.
  • Anonymous
    January 31, 2006
    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);
    }
    }
  • Anonymous
    February 01, 2006
    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...
  • Anonymous
    February 01, 2006
    I'm sorry my name is not Themes. The browser at my work have wrong cookies :-)
  • Anonymous
    February 03, 2006

    Comparison of AJAX frameworks for ASP.NET
    Extending CodeSmith merge functionality
    Good read on...
  • Anonymous
    March 16, 2006
    Comparison of AJAX frameworks for ASP.NET
    Extending CodeSmith merge functionality
    Good read on SQLCLR...
  • Anonymous
    April 23, 2006
    Comparison of AJAX frameworks for ASP.NET
    Extending CodeSmith merge functionality
    Good read on SQLCLR...