C# 为什么ReSharper会告诉我;隐式捕获闭包“;?
我有以下代码:C# 为什么ReSharper会告诉我;隐式捕获闭包“;?,c#,linq,resharper,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
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;
}
using System;
public class Class1 {
private Action _someAction;
public void Method() {
var obj1 = new object();
var obj2 = new object();
_someAction += () => {
Console.WriteLine(obj1);
Console.WriteLine(obj2);
};
// "Implicitly captured closure: obj2"
_someAction += () => {
Console.WriteLine(obj1);
};
}
}
public双重计算的ailyprojectpullforcemax(日期时间日期,字符串开始=null,字符串结束=null)
{
日志(“计算每日最大拉力…”;
var pullForceList=start==null
?_pullForce.Where((t,i)=>_date[i]==date.ToList()//隐式捕获的闭包:end,start
:_pullForce.Where(
(t,i)=>\u date[i]==date和DateTime.Compare(\u time[i],DateTime.Parse(start))>0和
比较(_-time[i],DateTime.Parse(end))<0.ToList();
_pullForceDailyMax=Math.Round(pullForceList.Max(),2,中点Rounding.AwayFromZero);
返回_pullForceDailyMax;
}
现在,我在这条线上添加了一条建议改变的评论。这意味着什么,或者为什么需要改变
隐式捕获的闭包:end,start警告告诉您变量end
和start
保持活动状态,因为此方法中的任何lambda保持活动状态
看看这个简短的例子
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();
}
我在第一个lambda中得到一个“隐式捕获闭包:g”警告。它告诉我只要第一个lambda在使用,g
就不可能存在
编译器为两个lambda表达式生成一个类,并将lambda表达式中使用的所有变量放入该类中
因此,在我的示例中,g
和i
被保存在同一个类中,用于执行我的委托。如果g
是一个遗留大量资源的重对象,垃圾收集器将无法回收它,因为只要有lambda表达式在使用,此类中的引用就仍然有效。这是一个潜在的内存泄漏,这就是R#警告的原因
@夹板
在C#中,匿名方法总是存储在每个方法的一个类中,有两种方法可以避免这种情况:
使用实例方法而不是匿名方法
将lambda表达式的创建拆分为两个方法
同意彼得·莫滕森的观点
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
}
}
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));
}
public-void-Captured()
{
ImplicitCaptureClosure.c__DisplayClass2CDisplayClass2=新的ImplicitCaptureClosure.c__DisplayClass2();
cDisplayClass2.x=新对象();
cDisplayClass2.store=新值store();
动作动作=新动作((对象)cDisplayClass2,_方法ptr(Capturedb__0));
Func Func=新Func((对象)cDisplayClass2,_methodptr(Capturedb__1));
}
尽管第二个lambda不使用x
,但它不能被垃圾收集,因为x
被编译为lambda中使用的生成类的属性。对于Linq to Sql查询,您可能会收到此警告。lambda的作用域可能比方法更有效,因为查询通常是在方法超出作用域后实现的。根据您的情况,您可能希望在方法中实现结果(即通过.ToList()),以允许对L2S lambda中捕获的方法实例变量进行GC。警告有效,并在具有多个lambda的方法中显示,并且它们捕获不同的值
调用包含lambdas的方法时,编译器生成的对象将使用以下命令实例化:
- 表示lambda的实例方法
- 表示任何lambda捕获的所有值的字段
例如:
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++; });
}
}
类反编译
{
反编译(Action callable1,Action callable2)
{
变量p1=1;
var p2=“你好”;
callable1(()=>p1++);//警告:隐式捕获的闭包:p2
callable2(()=>{p2.ToString();p1++;});
}
}
检查该类生成的代码(稍微整理):
类反编译
{
反编译(Action callable1,Action callable2)
{
var helper=new LambdaHelper();
1.p1=1;
helper.p2=“你好”;
callable1(helper.Lambda1);
callable2(helper.Lambda2);
}
[编译生成]
私密密封级兰伯德黑尔
{
公共int p1;
公共字符串p2;
public void Lambda1(){++p1;}
public void Lambda2(){p2.ToString();++p1;}
}
}
注意创建的LambdaHelper
实例同时存储p1
和p2
想象一下:
callable1
保留对其参数helper.Lambda1
callable2
不保留对其参数的引用,helper.Lambda2
在这种情况下,对helper.Lambda1
的引用也间接引用了p2
中的字符串,这意味着垃圾收集器将无法取消分配该字符串。最坏的情况是内存/资源泄漏。或者,它可能会使对象比需要的时间更长,如果对象从第0代升级到第1代,这可能会对GC产生影响。只要单击下面的提示,您就可以找到提出建议的原因:
这个提示会指引你
本次检查提请您注意以下事实:更多的关闭
捕捉到的价值比明显可见的要多,这有一个
对这些值的生命周期的影响
考虑以下代码:
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;
}
using System;
public class Class1 {
private Action _someAction;
public void Method() {
var obj1 = new object();
var obj2 = new object();
_someAction += () => {
Console.WriteLine(obj1);
Console.WriteLine(obj2);
};
// "Implicitly captured closure: obj2"
_someAction += () => {
Console.WriteLine(obj1);
};
}
}
在第一个闭包中,我们看到obj1和obj2都被显式捕获;我们可以通过查看代码看到这一点。对于
第二个闭包,我们可以看到obj1被显式捕获,
但是ReSharper警告我们obj2是隐式的c
public class Class1 {
[CompilerGenerated]
private sealed class <>c__DisplayClass1_0
{
public object obj1;
public object obj2;
internal void <Method>b__0()
{
Console.WriteLine(obj1);
Console.WriteLine(obj2);
}
internal void <Method>b__1()
{
Console.WriteLine(obj1);
}
}
private Action _someAction;
public void Method()
{
// Create the display class - just one class for both closures
var dc = new Class1.<>c__DisplayClass1_0();
// Capture the closure values as fields on the display class
dc.obj1 = new object();
dc.obj2 = new object();
// Add the display class methods as closure values
_someAction += new Action(dc.<Method>b__0);
_someAction += new Action(dc.<Method>b__1);
}
}