我有一个班级Test
,其中包含两个成员,一个(arr
),其中大量内存,另一个(b
) #39; T:
public class Test
{
public Test() {
Arr = new int[100000000];
}
public bool B {get; private set;}
public int[] Arr {get; private set;}
}
稍后在我的代码中,我想以这种方式存储lambda表达式:
// `test` has been declared somewhere as an instance of Test
Action lambda = () => {
if (test.B)
// Do things
}
这个闭包的内存消耗是多少?
它是否会在其环境中保留整个Test
对象,还是只保留Test.b
?
我应该这样做:
var tmpB = test.B;
Action lambda = () => {
if (tmpB)
// Do things
}
答案 0 :(得分:5)
闭包将存储test
变量的值,而test
变量只是引用到其他地方类型为Test
的对象在内存中,因为它不是struct
,并且Test
对象实际上没有整数数组,所以它只是对存储在中的大数组的引用内存中的另一个位置。
由于您持有对该Test
实例的引用,只要该关闭不符合垃圾回收条件,该对象就无法进行垃圾回收。如果您从Test
对象中拉出布尔值并关闭 ,如您所示,则您不再引用Test
对象。因此,如果没有任何东西可以访问Test
实例或包含的数组,那么它就有资格进行垃圾回收。如果仍然有其他代码可以访问它,那么情况就不会如此,并且没有任何好处。
答案 1 :(得分:5)
它会将整个Test对象保存在其环境中,还是只保存Test.b?
好吧,它会捕获变量 test
(通过创建一个单独的类来包含该变量),而后者又有一个值,它是{{{{{ 1}}。
换句话说,这样的方法:
Test
将转换为这样的内容:
public Action Foo()
{
Test test = new Test();
Action printB = () => Console.WriteLine(test.b);
return printB;
}
所以是的,如果你不希望委托有效地保持public Action Foo()
{
CompiledGeneratedClass tmp = new CompilerGEneratedClass();
tmp.test = new Test();
Action printB = tmp.GeneratedMethod;
return printB;
}
private class CompilerGeneratedClass
{
public Test test;
public void GeneratedMethod()
{
Console.WriteLine(test.b)
}
}
的实例存活,你应该首先提取属性的值。请注意,这有两个语义差异:
Test
本身的值发生变化(例如,引用test
的不同实例),则不会再在代理中看到