Introduction
In Part I of this series, we looked at what .NET delegates are, and how they can be used to hold methods in variables, or to pass methods as parameters into other methods.
In this article, we are going to look at how we can use anonymous methods and lambdas, to make our code more terse by reducing the amount of code we need to write.
Anonymous Methods
Firstly, here is a recap of our SimpleCalc.exe
code from Part I:
// C#
delegate int MathFunction(int int1, int int2);
static int Add(int int1, int int2)
{
return int1 + int2;
}
static int Subtract(int int1, int int2)
{
return int1 - int2;
}
static int Multiply(int int1, int int2)
{
return int1 * int2;
}
static int Divide(int int1, int int2)
{
return int1 / int2;
}
static void PrintResult(MathFunction mathFunction, int int1, int int2)
{
int result = mathFunction(int1, int2);
Console.WriteLine(String.Format("Result is {0}", result));
}
static void Main(string[] args)
{
int left = int.Parse(args[0]);
char theOperator = args[1][0];
int right = int.Parse(args[2]);
MathFunction mathFunction;
if (theOperator == '+')
mathFunction = Add;
else if (theOperator == '-')
mathFunction = Subtract;
else if (theOperator == '*')
mathFunction = Multiply;
else
mathFunction = Divide;
PrintResult(mathFunction, left, right);
}
' Visual Basic
Delegate Function MathFunction(ByVal int1 As Integer, ByVal int2 As Integer) As Integer
Function Add(ByVal int1 As Integer, ByVal int2 As Integer) As Integer
Return int1 + int2
End Function
Function Subtract(ByVal int1 As Integer, ByVal int2 As Integer) As Integer
Return int1 - int2
End Function
Function Multiply(ByVal int1 As Integer, ByVal int2 As Integer) As Integer
Return int1 * int2
End Function
Function Divide(ByVal int1 As Integer, ByVal int2 As Integer) As Integer
Return CInt(int1 / int2)
End Function
Sub PrintResult(ByVal mathFunction As MathFunction, ByVal int1 As Integer, ByVal int2 As Integer)
Dim result As Integer = mathFunction(int1, int2)
Console.WriteLine(String.Format("Result is {0}", result))
End Sub
Sub Main(ByVal args() As String)
Dim left As Integer = Integer.Parse(args(0))
Dim theOperator As Char = args(1)(0)
Dim right As Integer = Integer.Parse(args(2))
Dim mathFunction As MathFunction
If theOperator = "+" Then
mathFunction = AddressOf Add
ElseIf theOperator = "-" Then
mathFunction = AddressOf Subtract
ElseIf theOperator = "*" Then
mathFunction = AddressOf Multiply
Else
mathFunction = AddressOf Divide
End If
PrintResult(mathFunction, left, right)
End Sub
As you can see, each method that we want to assign to a variable of type MathFunction
has been declared as a specific, named method (e.g.: Add()
, Subtract()
etc.). Now, it would be good if we could specify our MathFunction
methods anonymously and in-line as we pass them into our PrintResult()
method.
Well, this we can do as follows:
// C#
static void Main(string[] args)
{
int left = int.Parse(args[0]);
char theOperator = args[1][0];
int right = int.Parse(args[2]);
if (theOperator == '+')
PrintResult(delegate(int int1, int int2) { return int1 + int2; }, left, right);
else if (theOperator == '-')
PrintResult(delegate(int int1, int int2) { return int1 - int2; }, left, right);
else if (theOperator == '*')
PrintResult(delegate(int int1, int int2) { return int1 * int2; }, left, right);
else
PrintResult(delegate(int int1, int int2) { return int1 / int2; }, left, right);
}
Note how the parameters required by our anonymous methods are enclosed between the parentheses; and the main body of our methods are enclosed in curly braces. This removes the need to have named methods for each of our mathematical functions, considerably reducing the number of lines of code.
If you are wondering why there is no Visual Basic example, it is because Visual Basic currently does not support anonymous methods. It does, however, support the use of Lambdas.
Lambdas
A statement lambda is, to all intents and purposes, simply a shorthand way of writing an anonymous method. We can thus shorten our code further by changing our anonymous methods into statement lambdas, as follows:
// C#
static void Main(string[] args)
{
int left = int.Parse(args[0]);
char theOperator = args[1][0];
int right = int.Parse(args[2]);
if (theOperator == '+')
PrintResult((int1, int2) => { return int1 + int2; }, left, right);
else if (theOperator == '-')
PrintResult((int1, int2) => { return int1 - int2; }, left, right);
else if (theOperator == '*')
PrintResult((int1, int2) => { return int1 * int2; }, left, right);
else
PrintResult((int1, int2) => { return int1 / int2; }, left, right);
}
' Visual Basic
Sub Main(ByVal args() As String)
Dim left As Integer = Integer.Parse(args(0))
Dim theOperator As Char = args(1)(0)
Dim right As Integer = Integer.Parse(args(2))
If theOperator = "+" Then
PrintResult(Function(int1, int2)
Return int1 + int2
End Function, left, right)
ElseIf theOperator = "-" Then
PrintResult(Function(int1, int2)
Return int1 - int2
End Function, left, right)
ElseIf theOperator = "*" Then
PrintResult(Function(int1, int2)
Return int1 * int2
End Function, left, right)
Else
PrintResult(Function(int1, int2)
Return CInt(int1 / int2)
End Function, left, right)
End If
End Sub
Note how the types of our int1
and int2
parameters are now automatically inferred by the compiler.
We can now reduce our code even further by using expression lambdas instead of statement lambdas. Expression lambdas are single-line lambdas which implicitly return a value. In our example, they would look as follows:
// C#
static void Main(string[] args)
{
int left = int.Parse(args[0]);
char theOperator = args[1][0];
int right = int.Parse(args[2]);
if (theOperator == '+')
PrintResult((int1, int2) => int1 + int2, left, right);
else if (theOperator == '-')
PrintResult((int1, int2) => int1 - int2, left, right);
else if (theOperator == '*')
PrintResult((int1, int2) => int1 * int2, left, right);
else
PrintResult((int1, int2) => int1 / int2, left, right);
}
' Visual Basic
Sub Main(ByVal args() As String)
Dim left As Integer = Integer.Parse(args(0))
Dim theOperator As Char = args(1)(0)
Dim right As Integer = Integer.Parse(args(2))
If theOperator = "+" Then
PrintResult(Function(int1, int2) int1 + int2, left, right)
ElseIf theOperator = "-" Then
PrintResult(Function(int1, int2) int1 - int2, left, right)
ElseIf theOperator = "*" Then
PrintResult(Function(int1, int2) int1 * int2, left, right)
Else
PrintResult(Function(int1, int2) CInt(int1 / int2), left, right)
End If
End Sub
Note how the body of the lambda is no longer enclosed within an explicit code block; and that the the explicit return
statement has been removed.
Summary
Anonymous methods (C# only) and lambdas (C# and VB.NET) allow us to write much more concise code by removing the need to declare and name each method specifically.
Next: Generic Delegates
While your article is top notch, I miss the note about the code beeing shorter but less readable. Thus Lambdas (and anonymous methods) should be used in small doses.
ReplyDeleteThanks for that article!