动作/ Lambda表达式内存管理问题

don*_*ngg 8 c# memory-management .net-4.0

我在一个局部变量中存储一个动作,然后我在该局部变量超出范围之后使用.在使用它之前是否有被清理的危险?这是一个例子:

public List<object> GetMaps() {
    Action<Customer1, Customer2> baseMap = (Customer1 c1, Customer2 c2) => {
        c2.FirstName = c1.FirstName;
    };

    var list = new List<object>() {
        new Action<SpecialCustomer1 c1, SpecialCustomer2 c2>() {
            baseMap(c1, c2);
            c2.SpecialProperty = c1.SpecialProperty;
        },
        new Action<SpecialCustomer1 c1, SpecialCustomer2 c2>() {
            baseMap(c1, c2);
            c2.SpecialProperty2 = c1.SpecialProperty2;
        },
    };

    return list;
}
Run Code Online (Sandbox Code Playgroud)

因此,您可以在此示例中看到该函数正在返回调用的操作列表baseMap. baseMap只是一个局部变量.是否在其他操作中调用它足以让.NET知道不清理它?

Eri*_*ert 13

我推荐你参考C#4规范的第5.1.7节,它说:

如果局部变量由匿名函数捕获,则其生存期至少会延长,直到从匿名函数创建的委托或表达式树以及引用捕获变量的任何其他对象都有资格进行垃圾回收.

即使控制通过了本地范围的结束,也延长了本地的生命周期.在实践中,我们通过将local转换为闭包类的字段,然后通过在委托(或表达式树)中引用它来使类保持活动来实现此目的.

请注意,你可能遇到相反的问题; 有时事情比你想要的更长寿:

Expensive expensive = new Expensive();
Cheap cheap = new Cheap();
Action longlived = ()=>M(cheap);
Action shortlived = ()=>M(expensive);
Run Code Online (Sandbox Code Playgroud)

今天在C#中的工作方式是为两个代表生成的闭包只要长寿代表的生命周期保持"廉价"和"昂贵",即使长寿代表实际上并不使用"昂贵" "!(VB,JScript和许多其他有闭包的语言也有这个问题.)

我们在这里可以做的是在编译器中检测这种情况并创建两个闭包.我们正在考虑为未来版本的C#做这件事.


Dan*_*rth 5

不,它没有危险.使用从该匿名方法之外的一个局部变量的匿名方法会被编译成一个新的类,节省了一个字段,局部变量和对应于匿名方法的方法.

在您的情况下,这将创建如下所示的内容:

class ModifiedClosure
{
    private Action<Customer1, Customer2> _baseMap;
    public ModifiedClosure(Action<Customer1, Customer2> baseMap)
    {
        _baseMap = baseMap;
    }

    public void Method(SpecialCustomer1 c1, SpecialCustomer2 c2)
    {
        _baseMap(c1, c2);
        c2.SpecialProperty = c1.SpecialProperty;
    }
}
Run Code Online (Sandbox Code Playgroud)

列表初始化将看起来像这样:

Action<Customer1, Customer2> baseMap = (c1, c2) => c2.FirstName = c1.FirstName;

var list = new List<object>()
{
    (Action<SpecialCustomer1, 
            SpecialCustomer2>)(new ModifiedClosure(baseMap).Method),
    // ...
};
Run Code Online (Sandbox Code Playgroud)

顺便说一句:你的语法有点偏.列表创建将无法编译.它应该如下所示:

var list = new List<object>()
{
    (Action<SpecialCustomer1, SpecialCustomer2>)((c1, c2) =>
    {
        baseMap(c1, c2);
        c2.SpecialProperty = c1.SpecialProperty;
    }),
    (Action<SpecialCustomer1, SpecialCustomer2>)((c1, c2) =>
    {
        baseMap(c1, c2);
        c2.SpecialProperty2 = c1.SpecialProperty2;
    })
};
Run Code Online (Sandbox Code Playgroud)