Lambda =>使用块的事件处理程序

时间:2014-01-16 08:53:28

标签: c# events lambda using

说我的代码在lambda expressions中使用了多个using-blocks

我已经阅读了由于可能memory leaks而取消订阅事件处理程序。

  • 是否有必要在using-blocks中执行此操作,因为using确保对象被处置?

  • 这样编码是一种不好的做法吗? (我发现它很方便,因为它将所有内容保持在一起,而不是让一个类充满不同的事件处理程序

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

5 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:1)

我使用这样的lambdas开始,并且一旦复杂性增加就将它们重构为方法。这段代码存在一些问题,但恕我直言,使用像这样的lambdas没有什么本质上的错误,因为它非常易读。

  1. 您不需要using。由于您只启动BackgroundWorker并且不等待它完成,因此使用将在它仍在运行时尝试Dispose()它。可能是其他东西都提到w并保持活着,但是你却错过了清理的机会。

  2. 在调试时,任何尝试在lambda中使用编辑并继续的尝试都将失败。如果您借此机会将lambda转换为方法,则不会发生这种情况。

  3. 我认为你对ProgressChanged和内部ExportProgressChanged使用lambd只是完美的,但是RunWorkerCompleted处于代码I'的最大端。期待。在我看来,DoWork太大而不能成为一个lambda。将其重构为方法。

  4. 最后,这里有一个很好的机会来重构你所拥有的SqlBackupWorkerWithProgress。这将巩固您的代码的使用。

答案 2 :(得分:0)

 foreach(var eventHandler in MyEvent)
    eventHandler.Dispose()
  • 处置资源始终是资源持有者的责任;事件持有者有责任确保事件处理人员得到妥善处理(见上文)

  • 我不会对事件使用如此大的内联函数,我宁愿创建一个单独的函数。大lambdas是一个眼睛。

答案 3 :(得分:0)

您不应该以这种方式使用Block for BackgroundWorker。在您的示例中,工作程序将在RunWorkerAsync调用之后立即处理,可能导致后台处理停止。

声明一个属性以保持对BackgroundWorker的引用。在RunWorkerCompleted处理程序中将worker和set属性置为null。

由于事件提供程序保留对事件订阅者的引用,导致事件订阅者在事件提供者处于活动状态时不被垃圾回收,因此可能发生内存泄漏。

在该特定样本中,无需取消订阅事件,因为订阅者(处理事件的表单/代码)的寿命应该比后台工作者长。

答案 4 :(得分:0)

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

当您向后执行操作时会导致内存泄漏 - 您创建一个对象,在lambda中引用它(以及它上面的机箱),然后将其添加(并引用)给事件处理程序。

即。具有事件处理程序的任何对象都保存对其事件处理程序中所有labmdas的引用,并依次保存对这些lambdas中引用的所有对象的引用。