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...