Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么ReSharper会告诉我;隐式捕获闭包“;?_C#_Linq_Resharper - Fatal编程技术网

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);
        }
    }