Pio*_*nom 291 c# linq resharper
我有以下代码:
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;
}
Run Code Online (Sandbox Code Playgroud)
现在,我在ReSharper建议改变的行上添加了评论.这是什么意思,或者为什么需要改变?implicitly captured closure: end, start
Con*_*ole 386
该警告告诉您变量end
并start
保持活动,因为此方法中的任何lambdas都保持活动状态.
看看简短的例子
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();
}
Run Code Online (Sandbox Code Playgroud)
我在第一个lambda处得到了一个"隐式捕获的闭包:g"警告.它告诉我,只要第一个lambda正在使用,g
就不能收集垃圾.
编译器为两个lambda表达式生成一个类,并将所有变量放在lambda表达式中使用的该类中.
所以在我的例子中g
,i
并且在同一个类中执行我的代理.如果g
是遗留了大量资源的重型对象,则垃圾收集器无法回收它,因为只要正在使用任何lambda表达式,此类中的引用仍然存在.所以这是潜在的内存泄漏,这就是R#警告的原因.
@splintor与在C#中一样,匿名方法总是存储在每个方法的一个类中,有两种方法可以避免这种情况:
使用实例方法而不是匿名方法.
将lambda表达式的创建拆分为两个方法.
Sma*_*kid 32
同意Peter Mortensen.
C#编译器只生成一个类型,它封装了方法中所有lambda表达式的所有变量.
例如,给定源代码:
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
}
}
Run Code Online (Sandbox Code Playgroud)
编译器生成的类型如下:
[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();
}
}
Run Code Online (Sandbox Code Playgroud)
该Capture
方法编译为:
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));
}
Run Code Online (Sandbox Code Playgroud)
虽然第二个lambda不使用x
,但它不能被垃圾收集,因为它x
被编译为lambda中使用的生成类的属性.
Dre*_*kes 30
警告有效并显示在具有多个lambda的方法中,并且它们捕获不同的值.
当调用包含lambdas的方法时,将使用以下代码实例化编译器生成的对象:
举个例子:
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++; });
}
}
Run Code Online (Sandbox Code Playgroud)
检查这个类的生成代码(整理一下):
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; }
}
}
Run Code Online (Sandbox Code Playgroud)
注意LambdaHelper
创建的商店的实例p1
和p2
.
设想:
callable1
保持对其论点的长期参考, helper.Lambda1
callable2
没有引用它的论点, helper.Lambda2
在这种情况下,引用helper.Lambda1
也间接引用了字符串p2
,这意味着垃圾收集器将无法解除分配.在最坏的情况下,它是内存/资源泄漏.或者,它可以使对象保持比其他需要更长的时间,如果它们从gen0升级到gen1,则可能对GC产生影响.
归档时间: |
|
查看次数: |
68873 次 |
最近记录: |