Paul-Sebastian Manole
paulsebastian.codes

paulsebastian.codes

Static anonymous functions: New with C# 9

Photo by Zach Vessels on Unsplash

Static anonymous functions: New with C# 9

Paul-Sebastian Manole's photo
Paul-Sebastian Manole
·Mar 7, 2022·

3 min read

Subscribe to my newsletter and never miss my upcoming articles

Yesterday I learned that C# 9 has static anonymous functions and that I should probably use them by default, and you should probably too. But there's a caveat, you can only capture const or static state within them.

Here is an example:

int nonStaticVar = 1;
DoSomething(x => x + nonStaticVar);

The above lambda will capture nonStaticVar from its calling environment, thus incurring at least one allocation in this case.

Because C# by default passes everything by value, the nonStaticVar in the lambda is a value-copy of nonStaticVar from the enclosing state. Even if nonStaticVar was a reference type and not a value type like int, a new reference would still have been created as a copy of the captured reference.

But this is not entirely all that C# does here. In fact, C# lambdas work a bit differently: C# actually creates a class behind the scenes that captures the enclosing state that the lambda accesses! This is called a closure.

What this means is that a class needs to be instantiated before executing the lambda or anonymous function, which we know means a heap allocation. Besides the overhead of the closure, C# also has the overhead of allocating a delegate for the anonymous function (basically a reference, or pointer in C-language, to that function along with its signature).

Introducing static anonymous functions

Static anonymous functions or methods were introduced in C# 9 to improve upon the state described above (pun intended), by removing the need for some allocations.

When you apply the static modifier to a lambda or anonymous function, it can only reference static or constant objects from the enclosing state!

Even if you use a non-static anonymous function inside a static one, it won't be able to capture anything, except the variables declared in the enclosing static anonymous function's scope, but nothing from another level up — which would have been possible if the two anonymous functions were both not static!

Here is an example:

const int constVar = 1;
DoSomething(static x => x + constVar);

Here, the lambda can capture constVar because it's a constant (constants are also static by definition).

But, something like the following wouldn't compile:

int nonStaticVar = 1;
DoSomething(static x => x + nonStaticVar); // <-- error!

This is because a static lambda is trying to capture a non-static variable. You will get an error along the lines of: error CS8821: A static anonymous function cannot contain a reference to 'this' or 'base', or similar.

What is the benefit?

How does this improve performance? Well, here is the feature specification where you can read about the motivation behind the feature, but basically by disallowing the capture of locals or instance state from the containing scope, one can prevent unexpected captures and unexpected additional allocations.

For instance, if your lambda captures an enclosing local scope variable, then C# will have to create a delegate and a closure that captures that variable, so 2 heap allocations.

If your lambda instead only captures an instance variable from the enclosing scope, then C# only creates a delegate because the enclosing type is already an object on the heap, so 1 extra heap allocation only.

With static anonymous functions, you would incur 0 heap allocations, because your lambda is not allowed to access non-static or non-const objects (which are allocated statically once)!

Take away

So, when possible, to avoid unnecessary and wasteful memory allocation, consider using static anonymous functions. I would actually go as far as writing all my lambdas with the static modifier first, to avoid unintentional capturing and only when Intellisense or the compiler warns me, would I remove it.

If you need to capture non-static state though, consider if you can make that state static or const first.

End note

I hope this wasn't too long or too boring to read. I want to keep my future posts shorter if possible. If it was, please tell me in the comments. Thanks for reading!

 
Share this

Impressum

Disclaimer

All content provided on this site is for informational purposes only. The owner of this blog makes no representations as to the accuracy or completeness of any information on this site or found by following any link on this site.

The owner will not be liable for any errors or omissions in this information nor for the availability of this information. The owner will not be liable for any losses, injuries, or damages from the display or use of this information.

This terms and conditions is subject to change at anytime with or without notice.