.NET do lambdas是否可以防止垃圾收集中使用的外部引用?

时间:2015-07-30 16:50:31

标签: c# .net lambda garbage-collection

以下是一个例子:

var task = Task.Run();
var func = () => task.Result;

因此,如果我松开任务引用并保持func引用,GC将收集任务并使func抛出空引用异常?

3 个答案:

答案 0 :(得分:9)

没有。匿名函数捕获变量,至少在委托或表达式树被垃圾收集时延长其生命周期。

从C#5规范,第7.15.5.1节:

  

当外部变量被匿名函数引用时,外部变量被匿名函数称为捕获。通常,局部变量的生命周期仅限于执行与之关联的块或语句(第5.1.7节)。但是,捕获的外部变量的生命周期至少会延长,直到从匿名函数创建的委托或表达式树符合垃圾回收的条件。

请注意,它是捕获的变量,而不是当时变量的值。因此,如果您将代码更改为:

var task = Task.Run(...);
var func = () => task.Result;
task = null;

...然后原始的Task 可以被垃圾收集,如果你调用它,委托将抛出异常,因为它将使用当前 task变量的值。

答案 1 :(得分:3)

task显然仍被引用 - 由你的lambda引用。所以GC不会碰它。

但要确保你的lambda也被引用到某处:-)否则,整个树将被垃圾收集。

答案 2 :(得分:3)

  

因此,如果我松开任务引用并保持func引用,GC将收集任务并使func抛出空引用异常?

GC永远不会将任何内容设置为null,因此它不会抛出NullReferenceException。

当您在lambda中捕获局部变量时,编译器会生成一个类型以保存字段中的值,并使用对该字段的引用替换对局部变量的引用。因此,只要您保留对func的引用,委托就会保留对包含task的对象的引用,因此task仍然被引用并且不会被收集。