Local Function VS Lambda C# 7.0

Local function vs Lambda C# 7.0

This was explained by Mads Torgersen in C# Design Meeting Notes where local functions were first discussed:

You want a helper function. You are only using it from within a single function, and it likely uses variables and type parameters that are in scope in that containing function. On the other hand, unlike a lambda you don't need it as a first class object, so you don't care to give it a delegate type and allocate an actual delegate object. Also you may want it to be recursive or generic, or to implement it as an iterator.

To expand on it some more, the advantages are:

  1. Performance.

    When creating a lambda, a delegate has to be created, which is an unnecessary allocation in this case. Local functions are really just functions, no delegates are necessary.

    Also, local functions are more efficient with capturing local variables: lambdas usually capture variables into a class, while local functions can use a struct (passed using ref), which again avoids an allocation.

    This also means calling local functions is cheaper and they can be inlined, possibly increasing performance even further.

  2. Local functions can be recursive.

    Lambdas can be recursive too, but it requires awkward code, where you first assign null to a delegate variable and then the lambda. Local functions can naturally be recursive (including mutually recursive).

  3. Local functions can be generic.

    Lambdas cannot be generic, since they have to be assigned to a variable with a concrete type (that type can use generic variables from the outer scope, but that's not the same thing).

  4. Local functions can be implemented as an iterator.

    Lambdas cannot use the yield return (and yield break) keyword to implement IEnumerable<T>-returning function. Local functions can.

  5. Local functions look better.

    This is not mentioned in the above quote and might be just my personal bias, but I think that normal function syntax looks better than assigning a lambda to a delegate variable. Local functions are also more succinct.

    Compare:

    int add(int x, int y) => x + y;
    Func<int, int, int> add = (x, y) => x + y;

What are the benefits of C# 7 local functions over lambdas?

The 1st website you linked mentions some benefits of local functions:

- A lambda causes allocation.

- There's no elegant way of writing a recursive lambda.

- They can't use yield return and probably some other things.

One useful use-case is iterators:

Wrong way:

public static IEnumerable<T> SomeExtensionMethod<T>(this IEnumerable<T> source) {
//Since this method uses deferred execution,
//this exception will not be thrown until enumeration starts.
if (source == null)
throw new ArgumentNullException();
yield return something;
}

Right way:

public static IEnumerable<T> SomeExtensionMethod<T>(this IEnumerable<T> source) {
if (source == null)
throw new ArgumentNullException();
return Iterator();

IEnumerable<T> Iterator() {
yield return something;
}
}

Performance of assigning a simple lambda expression or a local function to a delegate

With the current version of the compiler (Roslyn 2.8.0), the version with lambda is more efficient, because it caches the delegate.

Looking at the IL of code that has your two samples in separate methods, it's effectively:

sealed class HelperClass
{
public static readonly HelperClass Instance = new HelperClass();

public static Func<MyClass, string> CachedDelegate;

internal string LambdaByName(MyClass x)
{
return x.Name;
}

internal string LocalFunctionByName(MyClass x)
{
return x.Name;
}
}

void Lambda(IEnumerable<MyClass> myItems)
{
var lk = myItems.ToLookup(HelperClass.CachedDelegate ??
(HelperClass.CachedDelegate =
new Func<MyClass, string>(HelperClass.Instance.LambdaByName)));
}

void LocalFunction(IEnumerable<MyClass> myItems)
{
var lk = myItems.ToLookup(
new Func<MyClass, string>(HelperClass.Instance.LocalFunctionByName)));
}

Notice how Lambda allocates the delegate only once and uses a cached delegate afterwards, while LocalFunction allocates the delegate every time. This makes Lambda more efficient in this specific case.

Though there is a proposal on GitHub to change the compiler to make LocalFunction as efficient as Lambda.

Why Local Functions generate IL different from Anonymous Methods and Lambda Expressions?

Anonymous methods stored in delegates may be called by any code, even code written in different languages, compiled years before C# 7 came out, and the CIL generated by the compiler needs to be valid for all possible uses. This means in your case, at the CIL level, the method must take no parameters.

Local methods can only be called by the same C# project (from the containing method, to be more specific), so the same compiler that compiles the method will also be handled to compile all calls to it. Such compatibility concerns as for anonymous methods therefore don't exist. Any CIL that produces the same effects will work here, so it makes sense to go for what's most efficient. In this case, the re-write by the compiler to enable the use of a value type instead of a reference type prevents unnecessary allocations.

C# 8.0 local and static local function why using them is better or not?

I think it's pretty clear from the way C# is developed that readability/ease of use is one of the core pillars for the language. Majority of the new features don't really provide anything truly new, besides giving you an option to write things differently, hopefully more readable/easier.

Almost always you are able to pick something written with a new feature and rewrite it without using it. It doesn't make the new feature invalid.

From design notes related to Local Functions, the intention is stated:
https://github.com/dotnet/roslyn/issues/3911

We agree that the scenario is useful. You want a helper function. You
are only using it from within a single function, and it likely uses
variables and type parameters that are in scope in that containing
function. On the other hand, unlike a lambda you don't need it as a
first class object, so you don't care to give it a delegate type and
allocate an actual delegate object. Also you may want it to be
recursive or generic, or to implement it as an iterator.

In your given example, I find the original code more readable.
The support for older compilers doesn't matter, if my project targets a new compiler, why would I care about old ones? If I do, where is the end of it? C#7? C#6? 4?

Performance is secondary to readability/maintainability of the code, only when something proves to be a bottleneck for you, you start optimizing it.

So having the options is good.

More advantages to local functions come in when you compare it versus a lambda function, there's a great answer here:
Local function vs Lambda C# 7.0

Is local-function is not efficient as common function for a recursive method? (C#)

Local functions can, in fact, close over the outer scope of their containing methods. When this happens, the compiler generates types for them that are used to capture the variables of the containing scope; these new types are often stored on the heap, increasing memory pressure and leading to more garbage collections.

Increased garbage collections result in reduced performance. How significant that performance hit is can vary, and whether or not you should concern yourself with it is a debate for philosophers and software engineers far more experienced than I am.

For your specific scenario (a recursive local function), I would advise the following: If the local function references variables outside its own scope, refactor it so that it takes all the data it needs as arguments. In this way, it is no longer a closure, and the compiler won't generate a type for it that places additional memory pressure on the heap.

If this isn't practical, use a standard, non-local method.

How is local function in C#7.0 different from a foreach or a loop?


its doing a recursive call to the same method. We can easily achieve this with a normal foreach

That are two different things: tail recursion (method calling itself at the end of the method itself) or iterating over a collection. They are not always exchangeable, but they can be used to achieve the same end result.

The local function is nothing more that a class method, with a scope limited to the method body it is declared in.

Local functions are useful for many more than recursion. It is an easy way to prevent repeating code in several blocks.



Related Topics



Leave a reply



Submit