Reputation: 62492
I was recently surprised by the way the compiler caches delegates when use use non-capturing delegates and lambdas. Take the following code (available at SharpLab):
using System;
public class C
{
static void Use(int x, int y)
{
// #1 - Allocates delegate per call
SomeMethod(Add);
// #2 - Caches delegate, doesn't allocate each time
SomeMethod((x, y) => Add(x, y));
// #3 - Allocates delegate per call
SomeMethod(LocalAdd);
// #4 - Caches delegate, doesn't allocate each time
SomeMethod((a, b) => LocalAdd(a, b));
static int LocalAdd(int a, int b)
{
return a + b;
}
}
static int Add(int x, int y)
{
return x + y;
}
static int SomeMethod(Func<int, int, int> func)
{
return func(1, 2);
}
}
I was surprised to see that in case #1 and #3 the compiler doesn't bother to cache the Func
delegate it creates. I did wonder if the reason for #1 is down to historic reason (maybe this is how it was done before generics and lambdas), but the fact that passing the local function directly does the same suggests otherwise.
So, my question is, why doesn't the compiler cache the delegate it all cases? It seems odd that I have to use the more wordy lambda syntax in order to create a cached value. Why do I have to rewrite this:
SomeMethod(LocalAdd);
as
SomeMethod((a, b) => LocalAdd(a, b));
In order to get the compiler to cache the delegate and therefore generate less garbage?
Upvotes: 3
Views: 234
Reputation: 1062895
This topic has been discussed for quite some considerable time, and can be summarized as: changing it could present detectable changes that could lead to changes in behavior, and lead to bugs in code that currently works. The scenarios in which this applies are pretty niche, and relate to reference equality tests against the delegate instance.
The impacted code would be pretty negligible, but: non-zero, and usually the default is "retain compatibility", if an optimization can change behavior.
Upvotes: 3