Warm tip: This article is reproduced from serverfault.com, please click

c#-调用func时为什么会有内存分配

(c# - Why are there memory allocations when calling a func)

发布于 2018-03-15 12:25:15

我有以下程序,它们从两个静态方法构造一个本地Func。但是奇怪的是,当我分析该程序时,它分配了将近一百万个Func对象。为什么调用Func对象还创建Func实例?

在此处输入图片说明

public static class Utils
{
    public static bool ComparerFunc(long thisTicks, long thatTicks)
    {
        return thisTicks < thatTicks;
    }
    public static int Foo(Guid[] guids, Func<long, long, bool> comparerFunc)
    {
        bool a = comparerFunc(1, 2);
        return 0;
    }
}
class Program
{
    static void Main(string[] args)
    {
        Func<Guid[], int> func = x => Utils.Foo(x, Utils.ComparerFunc);
        var guids = new Guid[10];
        for (int i = 0; i < 1000000; i++)
        {
            int a = func(guids);
        }
    }
}
Questioner
Xiaoguo Ge
Viewed
0
Jon Skeet 2018-03-15 21:23:12

你正在使用方法组转换来创建Func<long, long, bool>用于comparerFunc参数的。不幸的是,C#5规范当前要求每次运行时都要创建一个新的委托实例。在C#5规范的6.6节中,描述了方法组转换的运行时评估:

分配了委托类型D的新实例。如果没有足够的内存来分配新实例,则将抛出System.OutOfMemoryException,并且不会执行其他步骤。

匿名函数转换(6.5.1)的部分包括以下内容:

将具有相同(可能为空)的一组已捕获外部变量实例的语义相同的匿名函数转换为相同的委托类型(但不是必需的),以返回相同的委托实例。

...但是方法组转换没有类似之处。

这个代码是手段允许进行优化,以使用单个委托实例对于每个所涉及的代表-和罗斯林一样。

Func<Guid[], int> func = x => Utils.Foo(x, (a, b) => Utils.ComparerFunc(a, b));

另一种选择是分配Func<long, long, bool>一次并将其存储在局部变量中。该局部变量将需要由lambda表达式捕获,这将阻止将Func<Guid[], int>其缓存-这意味着,如果你Main多次执行,则将在每个调用上创建两个新的委托,而较早的解决方案将尽可能合理地缓存。该代码更简单:

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);
}

所有这些使我感到难过,在最新版的ECMA C#标准中,将允许编译器缓存方法组转换的结果。我不知道什么时候/它是否这么做,但。