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

本文关键字:引用 外部 是否 lambdas net | 更新日期: 2023-09-27 18:05:40

下面是一个例子:

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

所以如果我丢失了任务引用并保留了函数引用,那么GC会收集任务并使函数抛出空引用异常吗?

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

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

来自c# 5规范,第7.15.5.1节:

当一个外部变量被匿名函数引用时,外部变量被称为被匿名函数捕获。通常,局部变量的生命周期被限制在与它相关联的块或语句的执行期间(§5.1.7)。但是,捕获的外部变量的生命周期至少会延长,直到从匿名函数创建的委托或表达式树有资格进行垃圾收集为止。

请注意,捕获的是变量,而不是该变量当时的值。所以如果你把代码改成:
var task = Task.Run(...);
var func = () => task.Result;
task = null;

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

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

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

所以如果我丢失了任务引用并保留了函数引用,那么GC会收集任务并使函数抛出空引用异常吗?

GC永远不会将任何东西设置为null,所以它不会抛出NullReferenceException。

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