In a previous post I talked about the potential use of local functions to replace comments. This generated some good discussion on Twitter and in the comments.
In this post I wanted to show another use of local functions to potentially improve readability.
Improving Iteration Code Readability
Consider the following simple console app:
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var names = new[]{ "Sarah Smith", "Gentry Jones", "Arnold Appleview" };
// Output names with surname first
foreach (var name in names)
{
var nameParts = name.Split(" ");
var firstName = nameParts[0];
var lastName = nameParts[1];
var formattedName = $"{lastName}, {firstName}";
Console.WriteLine(formattedName);
}
Console.ReadLine();
}
}
}
The code inside the for loop could be considered at a lower level of abstraction/more detailed than the rest of the code and the Main method itself.
We could take the contents of the for loop and refactor it into a private function in the class as follows:
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var names = new[] { "Sarah Smith", "Gentry Jones", "Arnold Appleview" };
foreach (var name in names)
{
OutputWithSurnameFirst(name);
}
Console.ReadLine();
}
private static void OutputWithSurnameFirst(string name)
{
var nameParts = name.Split(" ");
var firstName = nameParts[0];
var lastName = nameParts[1];
var formattedName = $"{lastName}, {firstName}";
Console.WriteLine(formattedName);
}
}
}
Notice the Main method is not mixing as many levels of abstraction/detail now – we have also been able to remove the comment because the method name OutputWithSurnameFirst describes what the comment used to. If we are reading the Main method, we don’t have to burden our concentration with the details of the how the names are output unless we want to.
This approach is fine, but it could be argued that we have “polluted” the class with a method that is only used once in the Main method. It could also be argued that declaration of the OutputWithSurnameFirst method is not as close to it’s use as a local method would be.
Let’s take a look next at a version of the code that instead uses a local method:
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var names = new[] { "Sarah Smith", "Gentry Jones", "Arnold Appleview" };
foreach (var name in names)
{
OutputWithSurnameFirst(name);
}
Console.ReadLine();
static void OutputWithSurnameFirst(string name)
{
var nameParts = name.Split(" ");
var firstName = nameParts[0];
var lastName = nameParts[1];
var formattedName = $"{lastName}, {firstName}";
Console.WriteLine(formattedName);
}
}
}
}
In the preceding example, the local method has taken the place of the class-level method, it has however made the overall length of the Main method longer. In this example it could be argued that the previous version is more readable.
Let’s take a look at another example next.
Improving C# Lambda Code Readability with Local Functions
In some cases, a local function may improve the readability of lambda function code.
Take the following initial code:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var strings = new[] { "Hello", "31414", "2HI9" };
foreach (var name in strings)
{
IEnumerable<bool> areUpperLetters = name.Select(x => char.IsLetter(x) && char.IsUpper(x));
Console.WriteLine($"{name} upper digits = {string.Join(",", areUpperLetters)}");
}
Console.ReadLine();
}
}
}
In the preceding code, the variable named areUpperLetters gives us a clue as to what the lambda does, which is good, often a well-named variable can really improve readability. This code could be refactored to a local functions as follows:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var strings = new[] { "Hello", "31414", "2HI9" };
foreach (var name in strings)
{
IEnumerable<bool> areUpperLetters = name.Select(IsUpperCaseLetter);
Console.WriteLine($"{name} upper digits = {string.Join(",", areUpperLetters)}");
}
Console.ReadLine();
static bool IsUpperCaseLetter(char c)
{
return char.IsLetter(c) && char.IsUpper(c);
}
}
}
}
Now the logic that was contained in the lambda has been moved to the local function called IsUpperCaseLetter.
To see local functions in action and also learn a whole heap of C# tips check out my C# Tips and Traps Pluralsight course. You can also start watching the course with a free trial
SHARE: