Putting the Fun in Local Functions
Tags: c, dotnet, programming
Published: 2020-02-28 05:44:11

---
title: "Putting the Fun in Local Functions"
description: Readability can be one of the most important components of writing clean code. With a language feature like local functions, you can transform those long comments and messy blobs of business logic into easily grokable one-liners.
tags: C#, dotnet, programming
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/226vwjz7u2x2r4edm4hs.jpg
published: true
---

Many popular languages support the use of [local functions](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/local-functions) and in C# 7, support for them was announced with relatively little fanfare. As someone that would consider themselves a C# power-user, I seldom took advantage of the feature until I realized just how much it could help with making code more readable, specifically in the context as a replacement for comments/hacks, unit tests, and in general just to clean things up.

## What are local functions exactly?

Local functions are private methods of a type that are nested in another member. They can only be called from their containing member. Local functions can be declared in and called from:

- Methods, especially iterator methods and async methods
- Constructors
- Property accessors
- Event accessors
- Anonymous methods
- Lambda expressions
- Finalizers

As with most things, sometimes, it's easier to just _show_ you what a local function looks like:

```language-csharp
public static IEnumerable<Address> SanitizeAddresses(List<Address> addresses)
{
      foreach(Address address in addresses) 
      {
           yield return Anonymize(address);
      }

      // This is a local function
      Address Anonymize(Address address) { ... }
}

```

## Cleaning Up Comments with Local Functions

![](http://rion.io/content/images/2020/02/cleaning.gif)

One of the first use cases that comes to mind that functions can help alleviate is any pesky sanitation or business logic rules, particularly those around string manipulation, etc. If you've worked in enough business applications, you've undoubtably seen something terrible with some massive comment as to _why_ it's being done:

```language-csharp
public static User ProcessUser(User user)
{
      // All names must conform to some crazy Dr. Seuss-eqsue rhyming scheme
      // along with every other character being placed by it's closest numerically
      // shaped equivalent
      var seussifyExpression = new Regex("...");
      user.Name = seussifyExpression.Replace(user.Name, "...");
      user.Name = user.Name
                      .Replace(..., ...)
                      .Replace(..., ...)
                      .Replace(..., ...);

      // Other processes omitted for brevity
 
      return user;
}
```

As you can see here, we have a series of chained replacements, some relying on strings, and others relying on regular expressions, which can make a method pretty clunky, especially if there's multiple operations to perform. Now, this is where you can define a local function to encapsulate all this business logic to replace your crazy comment:

```language-csharp
public static User ProcessUser(User user)
{
      SanitizeName(user)

      // Other processes omitted for brevity
 
      return user;

      void SanitizeName(User user)
      {
          var seussifyExpression = new Regex("...");
          user.Name = seussifyExpression.Replace(user.Name, "...");
          user.Name = user.Name
                          .Replace(..., ...)
                          .Replace(..., ...)
                          .Replace(..., ...);

          return user;
      }
}
```

You could easily name your local function whatever you like, even `ApplyBusinessLogicNamingRules()` and include any necessary comments for reasoning that you'd like within there (if you absolutely need to answer _why_ you are doing something), but this should help the rest of your code tell you what it's doing without a comment explicitly writing it all out.

## Going All Reading Rainbow with Local Functions

![](http://rion.io/content/images/2020/02/reading-rainbow.jpg)

If readability isn't the single most important thing about code, then it's damn close to the top.

LINQ is another popular area that local functions can assist with, especially if you have to do any type of crazy filtering logic over a series of records. You can define a series of local functions that can cover each step of your filtering process (or any process really), and more easily reason about your code from a readability perspective:

```language-csharp
public List<int> FindPrimesStartingWithASpecificLetter(List<int> numbers, int startingDigit)
{
    return numbers.Where(n => n > 1 && Enumerable.Range(1, n).Where(x => n % x == 0).SequenceEqual(new [] {1, n }))
                  .Where(n => $"{n}".StartsWith($"{startingDigit}"));
}  
```

While succinct, it doesn't exactly _read_ well. Let's take a gander at what it looks like after rubbing some local functions on it:

```language-csharp
public List<int> FindPrimesStartingWithASpecificLetter(List<int> numbers, int startingDigit)
{
    return numbers.Where(x => IsPrime(x) && StartsWithDigit(x, startingDigit));
    
    bool IsPrime(int n) => return n > 1 && Enumerable.Range(1, n).Where(x -> n % n == 0).SequenceEqual(new [] { 1, n }));
    bool StartsWithDigit(int n, int startingDigit) => return $"{n}".StartsWith($"{startingDigit}");
}  
```

As you can see, local functions are assisting with wrapping up all the ugly/nasty logic within their own tiny little functions. This is a really trivial case, but as you might imagine if you have lines-upon-lines of code that isn't touching anything outside of one method, it's likely a solid candidate for a local function.

## Testing, Testing, Lo-ca-lly!


![](http://rion.io/content/images/2020/02/testing.jpg)


If you've spent any amount of time writing tests, either unit or integration, you are probably familiar with the fabled [Arrange-Act-Assert](http://wiki.c2.com/?ArrangeActAssert) pattern, which is used to separate each piece of functionality when testing a given piece of code as follows:

- **Arrange** all necessary preconditions and inputs.
- **Act** on the object or method under test.
- **Assert** that the expected results have occurred.

As you might imagine, this could lend itself to the pattern quite well for complex test cases:

```language-csharp
public void IsThisAnArrangeActAssertLocalFunction()
{
     Arrange();
     Act();
     Assert();

     Arrange() { ... }
     Act() { ... }
     Assert() { ... }
}
```


Is it practical? Does it fit all use cases? Is it something that you'd ever find yourself using? The answers to all of these might be an overwhelming no, but it does seem like a scenario where local functions _could_ play a role.

## Choose Your Own Adventure

![](http://rion.io/content/images/2020/02/choose-your-own.jpg)

Local functions present a few interesting options that fit some scenarios better than others. As a replacement for large comments or very messy business logic - absolutely. In unit tests or little one liners - probably not. With most new features, especially those that are sugary, it's really up to you and your team to see if they work for you. While they may seem appealing in some situations, they also seem ripe for abuse, potentially cluttered methods, and other issues that would completely defeat the purpose of using them in the first place.

So, if you choose to go down this road of local functions, proceed with care.