Local Functions – What’s the Value?

A great feature of C# 7.0 are local functions. Local functions have the syntax of methods and can be used within the scope of methods, properties, constructors…
With articles showing this feature, I often see questions: “What is it good for?” “Why is this needed?” To understand the usefulness of local functions, some good examples are needed. I try to show these with this article.

Music Note - C# 7

Local Functions with the yield Statement

Let’s start with a simplified filter method Where. This implementation checks for
parameters resulting in an ArgumentNullException in case null is passed:

Invoking this method, the ArgumentNullException is not thrown when the query statement is defined, but – because of the delayed execution of yield, with the foreach iteration in line 4:

For having error information when it is needed, the Where method can be splitted into two methods. The Where method just checks the parameters without any yield statement included in the implementation, and invokes the WhereImpl method where the yield is done.

With this in place, the ArgumentNullException happens in line 2 where the error is more helpful.

Now, the Where method has nothing than a parameter check and an invocation of the real implementation. Here, local functions are of great help. The implementation is simpler compared to the private method – the local function Iterator can access variables from the outer scope, and thus here parameters are not needed:

Recursive Functions

Another scenario for local functions are recursive calls.

In the following implementation of the QuickSort method, Sort is a local function that is called recursively until the collection is sorted.

Using recursive calls with C# you need to be careful:

With C# you need to be careful with recursive calls. Contrary to functional programming languages like F#, the C# compiler does not tail call optimization where recursive method calls are converted to iterations to not consume call stack. With C# you can easily result in a StackOverflowException.

When does it end with the default stack configuration doing recursive calls with C#?

This simple sample that doesn’t need a lot of stack memory with every iteration ends after 24020 iterations with a StackOverflowException, so be careful doing recursive calls with C#.

Local Functions instead of Lambda Expressions

Recently I came across an older sample where a Lambda expression is used in the AsynchronousPattern method:

This one can be replaced by a local function as well:

This is just a matter of taste, but doesn’t look the syntax with the local function easier?

What are your thoughts on local functions with C# 7?

Have fun programming and learning,
Christian

Some more C# 7 articles:

C# 7 – What’s New

C# 7.0 Pattern Matching

C# 7.0 Out Vars and Ref Returns

C# 7.0 Expression Bodied Members

Tuples with C# 7.0

Binary Literals and Digit Separators

More information about C# 7 in my Workshops and Books

7 thoughts on “Local Functions – What’s the Value?

  1. Please see my post about C# 7 features (https://codingforsmarties.wordpress.com/2017/04/05/c-7-features-i-dont-like) and its follow-up specifically regarding local functions (https://codingforsmarties.wordpress.com/2017/04/27/local-functions-at-it-again). Your yield example is interesting, but it’s a lot of work just to throw an exception at declaration instead of invocation, especially since this is contrary to how the base Linq methods work.

    Like

    1. The base LINQ methods work in the same way. You probably had errors passing null because of different overloads. Just try this out:
      string[] data = { “one”, “two” };
      Func predicate = null;
      var q = data.Where(predicate);
      foreach (var item in q)
      {
      Console.WriteLine(item);
      }

      The exception happens with the initialization of q, not in the foreach.

      Cheers,
      Christian

      Like

      1. I’m quite familiar with how .Net works. My point in my post is that I don’t see much use in this feature. Examples that I’ve seen can be equally implemented using private (not local) methods or lambdas.

        Also, your example changes the expected behavior of a standard Linq function by throwing at a different point in execution. While I agree with your reason for doing this, the deviation from a common standard is enough for me to question the design. Developers that come after me and encounter this code may not expect it to behave the way it does, which could make it more difficult to debug.

        Liked by 1 person

  2. Of course, the local function can also be implemented as a private method or using lambdas. Sometimes it’s just a matter of taste.
    However, lambdas have additional overhead compared to local functions, and private methods can be called from other methods of the class as well. The scope is more restricted with local functions, and they can also access variables in the scope of the outer method (similar to lambdas but different to other private methods).

    I see your point in naming my method “Where”. Would you be fine if I would have named this method “Filter”? I just named it “Where” to demonstrate a simplified implementation of the Where method from the framework. However, the framework itself has multiple Where methods implemented (not just overloads, but implementations in different classes).

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s