Welcome to MSDN Blogs Sign in | Join | Help

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.

Published Monday, January 30, 2006 11:04 AM by jmstall
Filed under:

Comments

# re: Fun with yield, generics, and foreach

Monday, January 30, 2006 11:21 AM by Chris
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.

# re: Fun with yield, generics, and foreach

Monday, January 30, 2006 1:40 PM by jmstall
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?




# re: Fun with yield, generics, and foreach

Monday, January 30, 2006 2:30 PM by Sean Chase
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);
}









# re: Fun with yield, generics, and foreach

Tuesday, January 31, 2006 3:05 AM by Themes
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

# re: Fun with yield, generics, and foreach

Tuesday, January 31, 2006 4:32 AM by abhinaba
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

# re: Fun with yield, generics, and foreach

Tuesday, January 31, 2006 5:15 AM by Themes
Abhinaba, Console.WriteLine can be used as Action<string>. This makes you code even simpler:

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

# Interesting Finds

Tuesday, January 31, 2006 8:38 AM by Jason Haley

# re: Fun with yield, generics, and foreach

Tuesday, January 31, 2006 11:02 AM by jmstall
Those are some nice examples.

# Interesting Finds

Tuesday, January 31, 2006 12:13 PM by Jason Haley

# re: Fun with yield, generics, and foreach

Tuesday, January 31, 2006 1:57 PM by abhinaba
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);
}
}

# re: Fun with yield, generics, and foreach

Wednesday, February 01, 2006 5:12 PM by Oleg Mihailik
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...

# re: Fun with yield, generics, and foreach

Wednesday, February 01, 2006 5:14 PM by Oleg Mihailik
I'm sorry my name is not Themes. The browser at my work have wrong cookies :-)

# Links of the Week

Friday, February 03, 2006 11:37 AM by Jeff Barnes - MCSD

Comparison of AJAX frameworks for ASP.NET
Extending CodeSmith merge functionality
Good read on...

# Links of the Week

Thursday, March 16, 2006 4:30 PM by Jeff Barnes - MCSD
Comparison of AJAX frameworks for ASP.NET
Extending CodeSmith merge functionality
Good read on SQLCLR...

# Links of the Week

Sunday, April 23, 2006 12:32 PM by Jeff W. Barnes
Comparison of AJAX frameworks for ASP.NET
Extending CodeSmith merge functionality
Good read on SQLCLR...
New Comments to this post are disabled
 
Page view tracker