One idea I’ve been thinking about recently is the replacement of comments with local function calls.
Now this idea doesn’t mean that it’s ok to have massive functions that have no functional cohesion but instead in some circumstances it may improve readability.
In C#, local functions were introduced in C# 7.0. They essentially allow you to write a function inside of a method, property get/set, etc.
As an example take the following code:
public static void ProcessSensorData(string data)
{
// HACK: occasionally a sensor hardware glitch adds extraneous $ signs
data = data.Replace("$", "");
string upperCaseName = data.ToUpperInvariant();
Save(upperCaseName);
}
private static void Save(string data)
{
// Save somewhere etc.
Console.WriteLine(data);
}
In the preceding code there is a hack to fix broken sensors that keep adding extra $ signs.
This could be written using a local function as follows:
public static void ProcessSensorData(string data)
{
FixExtraneousSensorData();
string upperCaseName = data.ToUpperInvariant();
Save(upperCaseName);
void FixExtraneousSensorData()
{
data = data.Replace("$", "");
}
}
Notice in this version, there is a local function FixExtraneousSensorData that strips out the $ signs. This function is named to try and convey the comment that we had before: “occasionally a sensor hardware glitch adds extraneous $ signs”. Also notice the local function has direct access to the variables of the method in which they’re declared, in this case data.
There are other options here of course such as creating a normal non-local class-level function and passing data to it, or perhaps creating and injecting a data sanitation class as a dependency.
Replacing Arrange, Act, Assert Comments in Unit Tests
As another example consider the following test code:
[Fact]
public void HaveSanitizedFullName()
{
// Arrange
var p = new Person
{
FirstName = " Sarah ",
LastName = " Smith "
};
// Act
var fullName = p.CreateFullSanitizedName();
// Assert
Assert.Equal("Sarah Smith", fullName);
}
Notice the comments separating the logical test phases.
Again these comments could be replaced with local functions as follows:
[Fact]
public void HaveSanitizedFullName_LocalFunctions()
{
Person p;
string fullName;
Arrange();
Act();
AssertResults();
void Arrange()
{
p = new Person
{
FirstName = " Sarah ",
LastName = " Smith "
};
}
void Act()
{
fullName = p.CreateFullSanitizedName();
}
void AssertResults()
{
Assert.Equal("Sarah Smith", fullName);
}
}
This version is a lot longer and although we’ve rid ourselves of the comments the test body is a lot longer, with more lines of code, and I think is probably not as readable. Obviously the test is very simple, if you’ve got a lot of test arrange code for example you could just abstract the arrange phase perhaps.
Another option in the test code to remove the comments is to make use of the most basic unit of design – white space. So for example we could remove comments and still give a clue to the various phases as follows:
[Fact]
public void HaveSanitizedFullName_WhiteSpace()
{
var p = new Person
{
FirstName = " Sarah ",
LastName = " Smith "
};
var fullName = p.CreateFullSanitizedName();
Assert.Equal("Sarah Smith", fullName);
}
I think the tactical use of local functions like in the first example to replace the hack comment may be more useful than replacing the (arguably extraneous) arrange act assert comments in tests.
Let me know in the comments if you think this is a good idea, a terrible idea, or something that you might use now and again.
If you want to fill in the gaps in your C# knowledge be sure to check out my C# Tips and Traps training course from Pluralsight – get started with a free trial.
SHARE: