Вы создаете новый объект делегата при каждом вызове. Неудивительно, что у этого есть изрядные накладные расходы.
Если вы используете лямбда-выражение, которое не захватывает this
, или какие-либо локальные переменные (в этом случае компилятор может кэшировать его в stati c field) или если вы явно создаете один экземпляр и сохраняете его в поле самостоятельно, большая часть накладных расходов уходит.
Вот измененная версия вашего теста:
public class Calculations
{
public Random RandomGeneration = new Random();
private Func<int> exprField;
public Calculations()
{
exprField = () => RandomGeneration.Next() * RandomGeneration.Next();
}
[Benchmark]
public void CalculateNormal()
{
var s = RandomGeneration.Next() * RandomGeneration.Next();
}
[Benchmark]
public void CalculateUsingFunc()
{
Calculate(() => RandomGeneration.Next() * RandomGeneration.Next());
}
[Benchmark]
public void CalculateUsingFuncField()
{
Calculate(exprField);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Calculate(Func<int> expr)
{
return expr();
}
}
И результаты на моей машине:
| Method | Mean | Error | StdDev |
|------------------------ |---------:|---------:|---------:|
| CalculateNormal | 27.61 ns | 0.438 ns | 0.388 ns |
| CalculateUsingFunc | 48.74 ns | 1.009 ns | 0.894 ns |
| CalculateUsingFuncField | 32.53 ns | 0.698 ns | 0.717 ns |
Итак, есть еще бит накладных расходов, но гораздо меньше, чем раньше.