You're using a method group conversion to create the Func<long, long, bool>
used for the comparerFunc
parameter. Unfortunately, the C# 5 specification currently requires that to create a new delegate instance each time it's run. From the C# 5 specification section 6.6, describing the run-time evaluation of a method group conversion:
A new instance of the delegate type D is allocated. If there is not enough memory available to allocate the new instance, a System.OutOfMemoryException is thrown and no further steps are executed.
The section for anonymous function conversions (6.5.1) includes this:
Conversions of semantically identical anonymous functions with the same (possibly empty) set of captured outer variable instances to the same delegate types are permitted (but not required) to return the same delegate instance.
... but there's nothing similar for method group conversions.
That means this code is permitted to be optimized to use a single delegate instance for each of the delegates involved - and Roslyn does.
Func<Guid[], int> func = x => Utils.Foo(x, (a, b) => Utils.ComparerFunc(a, b));
Another option would be to allocate the Func<long, long, bool>
once and store it in a local variable. That local variable would need to be captured by the lambda expression, which prevents the Func<Guid[], int>
from being cached - meaning that if you executed Main
many times, you'd create two new delegates on each call, whereas the earlier solution would cache as far as is reasonable. The code is simpler though:
Func<long, long, bool> comparer = Utils.ComparerFunc;
Func<Guid[], int> func = x => Utils.Foo(x, comparer);
var guids = new Guid[10];
for (int i = 0; i < 1000000; i++)
{
int a = func(guids);
}
All of this makes me sad, and in the latest edition of the ECMA C# standard, the compiler will be permitted to cache the result of method group conversions. I don't know when/whether it will do so though.