Sergio and the sigil

Designing With Lambdas - Part I

Posted by Sergio on 2008-04-12

When our programming language of choice gets a new feature, it's usually not that hard to start using that feature right away from a consumer's point of view.

I could use the introduction of generics in .Net 2.0 as an example. When I wrote my first C# 2.0 piece of code, it already made use of the existing generic classes and methods, especially the ones in the System.Collections.Generic namespace such as List<T> and Dictionary<TKey,TValue>.

But it took a little more time until I learned how to design my own classes offering generic functionality. Reaching the balance of when to create generic classes, when to create generic methods, or when not to use generics only comes with some exercise.

I think this will be the case with lambdas for many people, including myself. Detecting opportunities to apply lambdas can make all the difference between a class that is a joy to use and one that is just the same old thing.

Processing lines in a file

My first example will be a more concise and less error-prone way of processing lines in a text file. Consider this hopefully familiar piece of code.

using(StreamReader rd = File.OpenText("Data.txt"))
{
	string line = rd.ReadLine();
	while(line != null)
	{
		DoSomething(line);
		// do more stuff with the line text

		//move on
		line = rd.ReadLine();
	}
}

How many times have you written something like this over and over? I know I did. If I were to compare the various times I implemented this, I would probably notice that the only thing that is different is the logic inside the while block. This should be a clue that a delegate or lambda could help make this pattern reusable.

But how do we create a reusable method that performs this common task without providing the logic inside the while? The last paragraph gave away the answer: delegates.

Let's create a helper class with a method to encapsulate the pattern at hand.

public static class FileUtil
{
	public static void EachLine(string fileName, Action<string> process)
	{
		using(StreamReader rd = File.OpenText(fileName))
		{
			string line = rd.ReadLine();
			while(line != null)
			{
				process(line);
				line = rd.ReadLine();
			}
		}
	}
}

The body of the EachLine method is almost the same as the original implementation we started with. The difference, as expected, is that we replaced the DoSomething(line) with a call to process, which is a delegate of type Action<string>, meaning that it expects a function that accepts a single parameter of type string and does not have a return value.

Using our new method, we can rewrite the original example like this.

FileUtil.EachLine("Data.txt", line => DoSomething(line));

Not bad. In this particular case, because we are just forwarding the line parameter to DoSomething, the call can be further simplified taking advantage of C#'s new shortened delegate creation syntax.

FileUtil.EachLine("Data.txt", DoSomething );

There you have it. Hopefully this assists someone in their journey in this new lambda thing.