Why I Fell in Love With Local Functions in C#!
The Touchy Feely Part
Lambdas have been a fantastic addition to C# since way back when they were first introduced with C# 3.0, having provided the capabilities needed for LINQ and it’s assortment of extension methods. You can say that Lambdas may be the most significant improvement in any new version of C#.
Local Functions were introduced with C# 7.0 and are also transformative in how code can be written, thought not as much as Lambdas were. Still, many people don’t even know Local Functions exist, so let’s get the word out!
From a coding style perspective, I’ve always felt the syntax of a Lambda looks odd, and it took me a while to wrap my head around their intricacies to be fluent with them. Local Functions are just functions. You declare them like any other function, just they are declared inside of another function.
Another thing I love about Local Functions is that it allows you to hide functions from other members of the object.
The Technical Part
Local Functions and Lambdas share a lot in common. They can both access the same set of parameters, locals, instance and static items. They can both be used as a delegate, although Lambdas must always used as a delegate.
But there are some major differences. First, Local Functions do not require a delegate to be used when called as a normal function. This saves on object instantiation whenever the Local Function is used as a normal function. Calls to a Local Function can be inlined when used as a normal function, which can drastically increase the performance of your application. Lambdas can never be inlined and as such can be a significant detriment to performance.
Local Functions can be generic, while Lambdas cannot. This has all kinds of benefits, especially with recursion (which is very difficult with Lambdas to begin with). If you’ve got a weakly typed path through your code and need a strongly typed section of repeatable code, then a generic Local Function has you covered.
Local Functions can also be iterators using the yield return pattern. Lambdas are not re-entrant and thus cannot be iterators.
Local Function as a Mutating Iterator
Conclusion
Microsoft has a good write-up about the differences between Local Functions and Lambdas. There is also this excellent answer on Stack Overflow. Personally, the combination of stylistic and technical advantages really seals the deal for me. If you’ve got a quick scenario where you’re using a method group of another object as the delegate of an Action or Func<>, then a Local Function is overkill syntactically and technically. But that’s about the only use case where I personally use Lambdas now.