C# λ=>;使用块中的事件处理程序

C# λ=>;使用块中的事件处理程序,c#,events,lambda,using,C#,Events,Lambda,Using,假设我有一个代码,它在使用块的中使用多个lambda表达式 我读过一篇文章,由于可能的内存泄漏,应该取消订阅事件处理程序 由于使用可确保对象得到处置,因此有必要在使用块中执行此操作吗 这样编码是一种糟糕的做法吗?(我觉得它很方便,因为它将所有内容都放在一起,而不是让一个类填充不同的事件处理程序 using (BackgroundWorker w = new BackgroundWorker()) { w.WorkerSupportsCancellation = true; w.

假设我有一个代码,它在
使用块的
中使用多个
lambda表达式


我读过一篇文章,由于可能的
内存泄漏
,应该取消订阅事件处理程序

  • 由于
    使用
    可确保对象得到处置,因此有必要在
    使用块
    中执行此操作吗

  • 这样编码是一种糟糕的做法吗?(我觉得它很方便,因为它将所有内容都放在一起,而不是让一个类填充不同的事件处理程序

    using (BackgroundWorker w = new BackgroundWorker())
    {
        w.WorkerSupportsCancellation = true;
        w.WorkerReportsProgress = true;
    
        w.ProgressChanged += (f, g) =>
        {
            pbExport.Maximum = (int)g.UserState;
            pbExport.Value = g.ProgressPercentage;
        };
        w.RunWorkerCompleted += (c, d) =>
        {
            if ((!d.Cancelled) && ((bool)d.Result))
            {
                MSG.Text = "Backup completed !";
            }
            else
            {
                MSG.Foreground = Brushes.Red;
                MSG.Text = "Error";
            }
        };
    w.DoWork += (a, b) =>
        {
            using (MySqlConnection cn = new MySqlConnection(Statics._CS))
            {
                try
                {
                    cn.Open();
                    using (MySqlCommand cmd = new MySqlCommand())
                    {
                        cmd.Connection = cn;
                        using (MySqlBackup mb = new MySqlBackup(cmd))
                        {
                            mb.ExportInfo.IntervalForProgressReport = 50;
                            mb.ExportProgressChanged += (h, i) =>
                                {
                                    w.ReportProgress(i.CurrentTableIndex, i.TotalTs);
                                };
                            mb.ExportToFile("dbbackup");
                         }
                    }
                    b.Cancel = false;
                    b.Result = true;
                }
                catch (MySqlException)
                {
                    b.Cancel = true;
                    b.Result = false;
                    w.CancelAsync();
                }
            }
        };
        w.RunWorkerAsync();
    }
    
  • 处置资源始终是资源持有者的责任;事件持有者有责任确保其事件处理程序得到正确处置(见上文)

  • 我不会对事件使用如此大的内联函数,我更愿意创建一个单独的函数

  • 处置资源始终是资源持有者的责任;事件持有者有责任确保其事件处理程序得到正确处置(见上文)

  • 我不会对事件使用如此大的内联函数,我更愿意创建一个单独的函数


    • 这看起来是错误的。
      using
      块将在线程仍在执行时调用
      RunWorkerAsync
      后立即调用
      Dispose

      这看起来是错误的。
      using
      块将在线程仍在执行时调用
      RunWorkerAsync
      后立即调用
      Dispose

      您不应以这种方式将using block用于BackgroundWorker。在示例中,worker将在RunWorkerAsync调用后立即被释放,这可能导致后台处理停止

      声明一个属性以保持对BackgroundWorker的引用处于活动状态。在RunWorkerCompleted处理程序中释放worker并将属性设置为null

      内存泄漏可能发生,因为事件提供程序保留对事件订阅服务器的引用,导致事件订阅服务器在事件提供程序处于活动状态时不会被垃圾收集


      在该特定示例中,不需要取消订阅事件,因为订阅方(处理事件的表单/代码)应比后台工作线程寿命更长。

      您不应以这种方式将using block用于BackgroundWorker。在您的示例中,工作线程将在RunWorkerAsync调用后立即被释放,这可能会导致后台处理停止

      声明一个属性以保持对BackgroundWorker的引用处于活动状态。在RunWorkerCompleted处理程序中释放worker并将属性设置为null

      内存泄漏可能发生,因为事件提供程序保留对事件订阅服务器的引用,导致事件订阅服务器在事件提供程序处于活动状态时不会被垃圾收集


      在该特定示例中,不需要取消订阅事件,因为订阅服务器(处理事件的表单/代码)的寿命应该比后台工作程序长。

      如果您的lambda未在使用范围(或更广泛的局部变量范围)之外使用/引用/关闭,则它们不会导致内存泄漏

      内存泄漏是在向后执行操作时造成的—您创建一个对象,在lambda中引用它(及其上的附件),然后将其添加(并由引用)到事件处理程序中


      也就是说,任何具有事件处理程序的对象都会在其事件处理程序中保存对所有LabMDA的引用,并反过来保存对这些lambda中引用的所有对象的引用。

      如果您的lambda未在使用范围(或更广泛的局部变量范围)之外使用/引用/关闭,则它们不会导致内存泄漏

      内存泄漏是在向后执行操作时造成的—您创建一个对象,在lambda中引用它(及其上的附件),然后将其添加(并由引用)到事件处理程序中


      也就是说,任何具有事件处理程序的对象都会在其事件处理程序中保存对所有LabMDA的引用,反过来又保存对这些lambda中引用的所有对象的引用。

      我从这样的lambda开始使用,并在复杂性增加时将其重构为方法。这段代码存在一些问题,但我认为没有问题ng这样使用lambdas本质上是错误的,因为它非常可读

    • 您不需要使用
      只启动
      BackgroundWorker
      而不等待它完成,因此使用将尝试
      Dispose()
      当它仍在运行时,它将继续运行。可能是其他东西引用了w并使其保持活动状态,但无论如何,您都错过了清理的机会

    • 调试时在lambda中使用Edit and Continue的任何尝试都将失败。如果您抓住机会将lambda切换到方法,则不会发生这种情况

    • 我认为您对
      ProgressChanged
      和内部
      ExportProgressChanged
      使用lambda非常完美,但是
      RunWorkerCompleted
      已经达到了我预期的最大代码大小。我认为
      DoWork
      太大了,不能作为lambda保留。请将其重构为一个方法


    • 最后,这里有一个很好的机会将您拥有的重构为
      SqlBackupWorkerWithProgress
      。这将巩固您的代码的使用。

      我从这样的lambda开始,并在复杂性增加时将其重构为方法。这段代码有一些问题,但我认为根本没有什么问题像这样使用lambdas是错误的,因为它非常可读

    • 您不需要使用
      只启动
      BackgroundWorker
      而不等待它完成,因此使用将尝试
      Dispose()
      当它仍在运行时,它将继续运行。可能是其他东西引用了w并使其保持活动状态,但无论如何,您都错过了清理的机会

    • 任何使用“编辑并继续”的尝试都会导致
       foreach(var eventHandler in MyEvent)
          eventHandler.Dispose()