Lambdas can be useful for creating little utilities that can help you create more readable code. An example of creating an abstraction.
[Test]
public void should_use_classes_instead_of_better_stuff()
{
Looper.Loop(5, new Printer());
}
public class Looper
{
public static void Loop(int count, Printer print)
{
const int max_count = 5;
for (int i = 0; i < max_count; i++)
print.Output();
}
}
public class Printer
{
public void Output()
{
Console.WriteLine("Executing");
}
}
Output
Executing
Executing
Executing
Executing
Executing
As you can see a lot of work just to print out “Executing” string, and you may run into having to make a changes into your classes.
Let’s say the string printed to the console needs to change like this.
[Test]
public void should_use_classes_instead_of_better_stuff()
{
Looper.Loop(1, new Printer("Start"));
Looper.Loop(5, new Printer("Middle"));
Looper.Loop(1, new Printer("End"));
}
public class Looper
{
public static void Loop(int max_count, Printer print)
{
for (int i = 0; i < max_count; i++)
{
print.Output();
}
}
}
public class Printer
{
private readonly string _prefix;
public Printer(string prefix)
{
_prefix = prefix;
}
public void Output()
{
Console.WriteLine("{0} Executing", _prefix);
}
}
Output
Start Executing
Middle Executing
Middle Executing
Middle Executing
Middle Executing
Middle Executing
End Executing
Now let’s get into using closures.
[Test]
public void should_use_closures()
{
Action<string> print = (prefix => Console.WriteLine("{0} Executing", prefix));
Action<int, Action> loop = ((count, fn) =>
{
for (int i = 0; i < count; i++)
{
fn();
}
});
loop(1, () => print("Start"));
loop(5, () => print("Middle"));
loop(1, () => print("End"));
}
Output is the same as above.
It still is a little noisy in the parameter for print. So let’s clean this up.
[Test]
public void should_use_binding_lambda()
{
Action<string> print = (prefix => Console.WriteLine("{0} Executing", prefix));
Action<int, Action> loop = ((count, fn) =>
{
for (int i = 0; i < count; i++)
{
fn();
}
});
Func<string, Action> use_printer = (prefix => () => print(prefix));
loop(1, use_printer("Start"));
loop(5, use_printer("Middle"));
loop(1, use_printer("End"));
}
This is nice abstraction. It hides the for loop from the programmer.
It still is noisy. How can we clean this up. TADA! Extension to the rescue.
public static void Times(this int count, Action function)
{
for (int i = 0; i < count; i++)
{
function();
}
}
And the actual usage
[Test]
public void should_use_binding_lambda_and_extension()
{
Action<string> print = (prefix => Console.WriteLine("{0} Executing", prefix));
Action<int, Action> loop = ((count, fn) =>
{
for (int i = 0; i < count; i++)
{
fn();
}
});
Func<string, Action> use_printer = (prefix => () => print(prefix));
1.Times(use_printer("Start"));
5.Times(use_printer("Middle"));
1.Times(use_printer("End"));
}
Usage for all 4 iterations
// class objects
Looper.Loop(5, new Printer("Middle"));
//closure
loop(5, () => print("Middle"));
//binding on lambda
loop(5, use_printer("Middle"));
//extension
5.Times(use_printer("Middle"));
Now which one would you use?