以下是一个例子:
var task = Task.Run();
var func = () => task.Result;
因此,如果我松开任务引用并保持func引用,GC将收集任务并使func抛出空引用异常?
答案 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
仍然被引用并且不会被收集。