0

注意以下代码来自 asp.net。


如果我有下面的(写得不好)代码

AmazonS3 s3Client = Amazon.AWSClientFactory.CreateAmazonS3Client();

// ...
// details elided
// ...

BackgroundWorker worker = new BackgroundWorker();

worker.DoWork += new DoWorkEventHandler((s, args) =>
{
    s3Client.PutObject(titledRequest);
});

new Thread(() => worker.RunWorkerAsync()).Start();

s3Client垃圾收集器会足够聪明,在后台工作人员完成之前永远不会收集对象吗?


请注意,我在线程内启动后台工作人员只是为了修复在我直接关闭后台工作人员时在 asp.net 中引发的恼人错误。

4

2 回答 2

5

是的,它会。编译器将为您生成一个新类,其中包含您在闭包中引用的每个局部变量的字段。闭包体将被发送到该类的方法中,并且包含函数中的所有局部变量将由编译器重写以引用该闭包对象上的字段。

所有这些魔法都发生在编译时;运行时不需要知道任何关于它的信息。由于运行时已经足够聪明,不会收集作为委托目标的对象,因此闭包引用的本地对象的生命周期可以保证延长到生成的委托对象的生命周期。

为了说明,编译器将吐出如下内容:

[System.Runtime.CompilerServices.CompilerGeneratedAttribute]
internal class ClosureImplementation // See note 1
{
    public AmazonS3 s3Client;

    public void Method(object s, EventArgs args)
    {
        s3Client.PutObject(titledRequest); // See note 2
    }
}

然后,在您的方法中,它被发出:

ClosureImplementation closure = new ClosureImplementation();
closure.s3Client = Amazon.AWSClientFactory.CreateAmazonS3Client();

// ...

worker.DoWork += closure.Method;

笔记:

  1. 生成的类的名称由编译器选择;ClosureImplementation只是一个例子。
  2. 我没有足够的上下文来知道titledRequest从哪里来,所以我故意没有说明编译器将如何处理它。
于 2012-11-19T19:23:27.700 回答
3

简而言之,是的。

您正在为您的事件处理程序使用 lambda,并且您正在关闭一个局部变量。这意味着您正在使用定义 lambda 的块之外的范围内的变量。这意味着,当编译器将 lambda 转换为“真实”方法时(在类内部,具有真实名称和一个对象实例和所有)它将包含对您作为该类的字段关闭的局部变量的引用。这将使该对象保持在范围内,直到 lambda 不再在任何地方被引用或在任何地方执行。

于 2012-11-19T19:25:31.997 回答