在匿名委托中捕获的私有字段
-
29-10-2019 - |
题
由于delegate
捕获了可变的this._bar
,它是否隐含地保留了B
的实例?会通过事件处理程序引用B
的实例,并通过A
实例的实例捕获变量吗?
如果_bar
是AttachToAEvent
方法的局部变量,会有所不同吗?
由于在我的情况下,基因编码实例的寿命比基因编码实例的寿命长得多,并且远小于基因编码实例,所以我担心这样做会导致“内存泄漏”。
解决方案
通过查看由编译器生成的代码,这很容易理解,该代码类似于: 通用标签
可以清楚地看到,创建的委托是 instance 委托(将对象的实例方法作为目标),因此必须保留对该对象实例的引用。
由于委托捕获了变量this._bar,它是否隐式地保存到 B的实例?
实际上,匿名方法仅捕获
this
(而不是this._bar
)。从生成的代码可以看出,构造的委托确实将保留对B
实例的引用。它必须;每当执行委托时,如何按需读取该字段?请记住,捕获了变量,而不是值。因为在我的情况下,A的寿命更长,并且寿命更短 我比B实例要担心会导致“内存泄漏” 这个。
是的,您有充分的理由要做。只要
A
实例是可访问的,B
事件订阅者仍将是可访问的。如果您不想看待弱事件,则需要重写它,以便在不再需要该处理程序时将其注销。如果_bar是 AttachToAEvent方法?
是的,因为捕获的变量将成为本地基因标签代码而不是本地基因标签代码。 但是假设
bar
是一个实例方法,那么您的“问题”(如果您想这样想的话)就变得更糟了。现在,编译器需要生成一个事件侦听器,以“记住”本地和包含其的this
对象实例。这是通过创建一个闭包对象并将其(实际上是它的一个实例方法)作为委托的目标来实现的。 通用标签
其他提示
Ani的答案是正确的。总结并添加一些细节:
由于委托捕获了变量this._bar,它是否隐式地持有B的实例?
是的。 “此”被捕获。
是否将通过事件处理程序引用B的实例,并通过A的实例捕获变量?
是的
如果_bar是AttachToAEvent方法的局部变量,会有所不同吗?
是的。在这种情况下,闭包对象将保留在本地;本地将被实现为封闭的领域。
由于在我的情况下,A实例的寿命比B实例长得多,并且比B实例小得多,因此我担心这样做会导致“内存泄漏”。
您绝对有权利担心。您的情况已经很糟,但是实际上,当您使用两个匿名函数时,情况可能会严重得多。现在,同一局部变量声明空间中的所有匿名函数都共享一个公共闭包,这意味着 all 封闭的外部变量(包括“ this”)的生存期可以扩展到。有关详细信息,请参见我关于该主题的文章:
http://blogs.msdn.com/b/ericlippert/archive/2007/06/06/fyi-c-and-vb-closures-are-per-scope.aspx
我们希望在假设的C#未来版本中解决此问题;我们可以更好地对闭包进行分区,而不是创建一个大的闭包。但是,这不会很快发生。
此外,C#5的“异步/等待”功能也可能会加剧当地人的寿命长于预期的情况。我们谁都不会为此感到兴奋,但是正如他们所说,完美是超凡脱俗的敌人。对于如何调整异步块的代码源来改善这种情况,我们有一些想法,但是没有任何希望。
如果您将匿名方法添加到事件中并希望对其进行引用,则必须将事件设置为null或将委托存储在列表中,以便以后可以从事件中“-=“”。
但是,是的,您可以从附加到事件的委托中引用的对象中获取“内存泄漏”。