从C#中的事件和垃圾收集中分离匿名侦听器

时间:2012-03-06 20:18:23

标签: c# garbage-collection event-listener

假设我有一个名为Dialog的类,它扩展了Form。对话框上有一个文本框和一个OK按钮,当用户单击OK时,将通过事件返回文本框值:

public class Dialog: Form
{
    public delegate void onDialogValueReturned(object sender, DialogEventArgs e);
    public event onDialogValueReturned DialogValueReturned;
 .
 .
 .

  OKButton.Click += (sender, evt) =>
        {
            DialogEventArgs e = new DialogEventArgs();
            e.Value =myTextBox.Text;                
            DialogValueReturned(this, e);
            this.Close();
        };

在我的调用表单中,我在本地方法中实例化一个对话框:

  private void Foo()
  {
        Dialog D = new Dialog("blah blah");
        D.DialogValueReturned += (dialog, evt) =>
            {

               //do something with evt.Value

            };


        D.ShowDialog();
   }

用户可以在一天中将此对话框实例化数十次甚至数百次。

当作用域离开私有方法时,垃圾收集器是否会自动清除与对话框实例相关的所有内容,包括匿名侦听器的所有管道?

由于

2 个答案:

答案 0 :(得分:2)

活动的发布者保留对每个订阅者的强烈引用。如果发布者的寿命比订阅者长,那么订阅者将在发布者出现时固定在内存中。

在您的示例中,发布者仅存在于私有方法的范围内,因此在方法返回后的某个时刻,对话框和处理程序都将被垃圾收集。

我建议遵守dot net framework guidelines for publishing an event,建议使用受保护的虚拟方法来调用事件。

答案 1 :(得分:0)

匿名函数将导致成员函数的名称由编译器自动生成。编译器生成的名称将包含在C#中非法的字符,以确保您不能使用相同的名称为您的类中的其他成员命名。除此之外,它的行为与绑定到事件的普通方法完全相同,因此所涉及的所有资源都将被垃圾收集。

作为设计说明,由于您对从对话框返回的值感兴趣,我建议不要使用事件来通知对话窗口已关闭。相反,您可以将代码包装在静态方法中,例如,在其中打开对话框,等待事件循环直到对话框关闭并读取用户的输入,以更合适的格式返回输入数据进一步处理。这将要求您打开模态窗口。这是一个例子:

public class MyDialog : Form
{
    // We can make the constructor private, as this class is instantiated only
    // in the Show method.
    private MyDialog()
    {
    }

    // ...

    public class ReturnValue
    {
        public string Foo { get; set; }
        // ...
    }

    public static ReturnValue ShowModal(/* any params, if required */)
    {
        ReturnValue result = new ReturnValue();
        MyDialog dialog = new MyDialog();

        if(DialogResult.OK == dialog.ShowDialog(null))
        {
            // We can access private members like txtFoo since we are within the class.
            result.Foo = dialog.txtFoo.Text;

            // ...
        }

        return result;
    }
}