Fun with C# Extension Methods: Quick Loops
In my last post, I demonstrated an extension method for easy creation of ranges, using the 1.To(x) syntax, similar to the Ruby [1..x] syntax. Today I’m writing another copy of a Ruby idea which lets you do a quick loop using a terse and easy-to-read syntax.
Remember, if you want to create your own extension methods, you need to define a static class to put them in, like this:
public static class Extensions
Quick Loops
Rails is king of easy loops. You can write this:
3.times do { // block of statements }
Why not do the same in C# 3.0? It’s easy. Add this to your extensions class:
public static void Times(this int repeatCount, Action<int> action) { for (int i = 1; i <= repeatCount; i++) { action(i); } }
Now you can use this syntax in your program:
100.Times(t =>
{
Console.WriteLine("t:{0}", t);
});
t references the current counter position from 0 to the specified number.
Question: Which would be clearer to you if you were taking over someone else’s code – the for loop in the extension method, or the .Times syntax?
It might be unfamiliar to me at first, but once I’d gotten the hang of it, I would find the extension method syntax to be much easier to follow in most situations.
“Hey, Richard”, I hear you shout. “What if a variable were used, such as the number of rows? Would it still be clear?” Hmm, maybe not, methinks.
A quick example:
table1.Rows.Count.Times(t =>
{
Console.WriteLine("t:{0}, name:{1}", t, table1.Rows[t]["Name"]);
});
With bad variable naming, this could get unwieldy. But with good naming it still looks okay:
var noOfRows = table.Rows.Count; noOfRows.Times(rowNo => { var row = table.Rows[rowNo]; Console.WriteLine("rowNo:{0}, Name:{1}", rowNo, row["Name"]); });
This would be opposed to the more usual:
for (int rowNo = 0; rowNo < table.Rows.Count; rowNo++) { var row = table.Rows[rowNo]; Console.WriteLine("rowNo:{0}, Name:{1}", rowNo, row["Name"]); }
foreach (DataRow row in table.Rows) { var rowNo = row.Table.Rows.IndexOf(row); Console.WriteLine("rowNo:{0}, Name:{1}", rowNo, row["Name"]); }
Despite the long syntax of the for loop, and the backwards somersaults you need to do to get the row index in the for loop, with the Times syntax you still have to use a lamda expression, and mustn’t forget the semicolon at the end of the lamda expression block. With the usual loops you don’t. As with all things, you wouldn’t always use this syntax, but sometimes it would be a good alternative.
What’s next?
Ruby is full of neat little tricks like I’ve presented here, and personally, I love ‘em. I’ll write some more soon, including “inject”, “collect” and “each”.
This is my personal blog, where I express my thoughts and thinking about programming and software development.