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.