Problem
The following is the code I have:
public double CalculateDailyProjectPullForceMax(DateTime date, string start = null, string end = null)
{
Log("Calculating Daily Pull Force Max...");
var pullForceList = start == null
? _pullForce.Where((t, i) => _date[i] == date).ToList() // implicitly captured closure: end, start
: _pullForce.Where(
(t, i) => _date[i] == date && DateTime.Compare(_time[i], DateTime.Parse(start)) > 0 &&
DateTime.Compare(_time[i], DateTime.Parse(end)) < 0).ToList();
_pullForceDailyMax = Math.Round(pullForceList.Max(), 2, MidpointRounding.AwayFromZero);
return _pullForceDailyMax;
}
Now, on the line where ReSharper is suggesting a change, I’ve added a remark. What does it mean, and why is it necessary to modify it? end, start, end, start, start, start, start, start, start, start, start, start
Asked by PiousVenom
Solution #1
The warning informs you that the variables end and start will remain alive as long as any of the lambdas in this method do.
Take a look at the sample below.
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
int i = 0;
Random g = new Random();
this.button1.Click += (sender, args) => this.label1.Text = i++.ToString();
this.button2.Click += (sender, args) => this.label1.Text = (g.Next() + i).ToString();
}
At the first lambda, I get a “Implicitly captured closure: g” warning. It’s telling me that as long as the first lambda is active, g can’t be garbage collected.
The compiler creates a class for both lambda expressions and assigns all variables used in the lambda expressions to that class.
For the execution of my delegates, g and I are held in the same class in my example. The garbage collector will not be able to reclaim g if it is a large object with a lot of resources left behind, because the reference in this class is still alive as long as any of the lambda expressions is in use. So this is a potential memory leak, and that is the reason for the R# warning.
@splintor There are two ways to avoid this in C# because anonymous methods are always stored in one class per method.
Answered by quadroid
Solution #2
Peter Mortensen and I agreed.
Only one type is created by the C# compiler, which encapsulates all variables for all lambda expressions in a method.
For example, given the source code:
public class ValueStore
{
public Object GetValue()
{
return 1;
}
public void SetValue(Object obj)
{
}
}
public class ImplicitCaptureClosure
{
public void Captured()
{
var x = new object();
ValueStore store = new ValueStore();
Action action = () => store.SetValue(x);
Func<Object> f = () => store.GetValue(); //Implicitly capture closure: x
}
}
The compiler produces a type that looks like this:
[CompilerGenerated]
private sealed class c__DisplayClass2
{
public object x;
public ValueStore store;
public c__DisplayClass2()
{
base.ctor();
}
//Represents the first lambda expression: () => store.SetValue(x)
public void Capturedb__0()
{
this.store.SetValue(this.x);
}
//Represents the second lambda expression: () => store.GetValue()
public object Capturedb__1()
{
return this.store.GetValue();
}
}
The Capture method is written as follows:
public void Captured()
{
ImplicitCaptureClosure.c__DisplayClass2 cDisplayClass2 = new ImplicitCaptureClosure.c__DisplayClass2();
cDisplayClass2.x = new object();
cDisplayClass2.store = new ValueStore();
Action action = new Action((object) cDisplayClass2, __methodptr(Capturedb__0));
Func<object> func = new Func<object>((object) cDisplayClass2, __methodptr(Capturedb__1));
}
Though the second lambda does not use x, it cannot be garbage collected as x is compiled as a property of the generated class used in the lambda.
Answered by Smartkid
Solution #3
The warning is valid and appears in methods with several lambdas that each capture a separate value.
When a method with lambdas is called, a compiler-generated object is created with the following properties:
As an example:
class DecompileMe
{
DecompileMe(Action<Action> callable1, Action<Action> callable2)
{
var p1 = 1;
var p2 = "hello";
callable1(() => p1++); // WARNING: Implicitly captured closure: p2
callable2(() => { p2.ToString(); p1++; });
}
}
Examine the generated code for this class (tidied up a little):
class DecompileMe
{
DecompileMe(Action<Action> callable1, Action<Action> callable2)
{
var helper = new LambdaHelper();
helper.p1 = 1;
helper.p2 = "hello";
callable1(helper.Lambda1);
callable2(helper.Lambda2);
}
[CompilerGenerated]
private sealed class LambdaHelper
{
public int p1;
public string p2;
public void Lambda1() { ++p1; }
public void Lambda2() { p2.ToString(); ++p1; }
}
}
It’s worth noting that the LambdaHelper instance created stores both p1 and p2.
Imagine that:
In this case, the term “assistant” is used. Because Lambda1 indirectly refers the string in p2, the garbage collector will be unable to deallocate it. At worst it is a memory/resource leak. Alternatively it may keep object(s) alive longer than otherwise needed, which can have an impact on GC if they get promoted from gen0 to gen1.
Answered by Drew Noakes
Solution #4
This warning may appear while using Linq to Sql queries. Because the query is frequently actualized after the method is out of scope, the lambda’s scope may survive the method. To allow for GC on the function’s instance vars captured in the L2S lambda, you may want to materialize the results (i.e. via.ToList()) within the method, depending on your situation.
Answered by Jason Dufair
Solution #5
This hint will lead you to this location.
Answered by anatol
Post is based on https://stackoverflow.com/questions/13633617/why-does-resharper-tell-me-implicitly-captured-closure