Fun with C# local functions
The reason for this post is simple, this code is so brilliantly simple that I just had to write about it.
On the face of it, it isn’t doing much that is interesting, but it is showing off something very critical. It is both obvious and easy to reason about. And if you don’t have local functions, trying to do something like that will require you to jump through several hops and pretty much always generate code that the compiler and any static analysis tool will consider suspect.
The fact that the reference to the local function can be added and then remove also means that we can do things like this:
A self cleaning delegate, which is only usually possible with code trickery that force you to capture a variable that you have set to null and then set to the value you are trying to use.
I know that this isn’t a really a major thing, but it make certain very specific scenarios so much easier, so it is just a joy to see. And yes, the impetus for this code was actually seeing it used in our code and going Wow!
Comments
Are there allocation differences between local functions and lambdna functions/delegates? I suppose not, at least not if you reference any locals.
While I like local functions, in this specific case, isn't using a delegate variable and a lambda simpler?
Since it's a single delegate instance, unsubscribing will work fine.
(I also used expression body, but you can do that with a local function too.)
The self cleaning delegate example wouldn't look nearly as nice with a lambda, you're right about that.
@Sebastiaan There can be. Local functions can sometimes access closed-over local variables though a
struct
that's passed byref
. But that's not going to happen when you create a delegate pointing to that local function, like what's happening in the above code.And of course, just calling a local function does not require you to allocate a delegate (which again is not relevant in this case).
Svick, The issue is with the self cleaning, primarily. And that you don't have to explicitly provide the signature (
EventHandler
) in your case, but can let the compiler figure it out. There is also the nice part about having an explicit name there.The choice is between providing the delegate signature (
EventHandler
) and explicitly specifying the return type and parameter types (void
,object
,EventArgs
). I don't see much difference between the two, except that the delegate signature is shorter.Though for generic delegates (e.g.
Func<int, int, int>
vs.int
,int
,int
), local functions win when it comes to length.Comment preview