如何从事件处理程序中删除自己?

时间:2013-05-24 12:16:02

标签: c# events timer .net-3.5 lambda

我想要做的是基本上从事件中删除一个函数,而不知道函数的名称。

我有FileSystemWatcher。如果创建/重命名文件,则检查其名称。如果匹配,则将其移动到特定位置。但是,如果文件被锁定,它会使一个lambda附加到计时器的tick事件,等待文件未被锁定。如果不是,则移动文件,然后从事件处理程序中删除自身。我已经看到很多方法可以做到这一点,比如保留实例,或者创建一个命名方法。我不能在这里做任何一件事。我有什么选择?

2 个答案:

答案 0 :(得分:21)

没有简单的方法可以实现这一目标。

首选方法:

我不明白为什么你不能保存代表。您不必将实例保存为某个字段。它可以是由匿名事件处理程序捕获的局部变量:

EventHandler<TypeOfEventArgs> handler = null;
handler = (s, e) =>
{
    // Do whatever you need to do here

    // Remove event:
    foo.Event -= handler;
}

foo.Event += handler;

我想不出一个你不能用它的场景。

不保存委托的替代方法:

然而,如果你有这样的场景,那就太麻烦了 您需要找到已添加为事件处理程序的委托。因为你没有保存它,所以很难获得它。没有this来获取当前正在执行的方法的委托。

您也不能在事件上使用GetInvocationList(),因为访问其定义的类之外的事件仅限于添加和删除处理程序,即+=-=

也不可能创建新的委托。虽然您可以访问定义匿名方法的MethodInfo对象,但您无法访问声明方法的类的实例。此类由编译器自动生成并调用{{1}在匿名方法内部将返回正常方法定义的类的实例。

我找到的唯一方法是查找事件使用的字段(如果有)并在其上调用this。以下代码使用虚拟类来演示此内容:

GetInvocationList()

请注意,传递给void Main() { var foo = new Foo(); foo.Bar += (s, e) => { Console.WriteLine("Executed"); var self = new StackFrame().GetMethod(); var eventField = foo.GetType() .GetField("Bar", BindingFlags.NonPublic | BindingFlags.Instance); if(eventField == null) return; var eventValue = eventField.GetValue(foo) as EventHandler; if(eventValue == null) return; var eventHandler = eventValue.GetInvocationList() .OfType<EventHandler>() .FirstOrDefault(x => x.Method == self) as EventHandler; if(eventHandler != null) foo.Bar -= eventHandler; }; foo.RaiseBar(); foo.RaiseBar(); } public class Foo { public event EventHandler Bar; public void RaiseBar() { var handler = Bar; if(handler != null) handler(this, EventArgs.Empty); } } 的字符串"Bar"必须是事件使用的字段的确切名称。这导致两个问题:

  1. 该字段的名称可能不同,例如使用显式事件实现时。您需要手动找到字段名称。
  2. 可能根本没有任何领域。如果事件使用显式事件实现并且只是委托给另一个事件或以其他方式存储代理,则会发生这种情况。
  3. 结论:

    替代方法依赖于实现细节,因此如果可以避免,请不要使用它。

答案 1 :(得分:0)

使用lambda表达式删除事件处理程序的步骤:

public partial class Form1 : Form
{
    private dynamic myEventHandler;
    public Form1()
    {
        InitializeComponent();
    }
    private void Form1_Load(object sender, EventArgs e)
    {
        myEventHandler = new System.EventHandler((sender2, e2) => this.button1_Click(sender, e, "Hi there"));
        this.button1.Click += myEventHandler;
    }

    private void button1_Click(object sender, EventArgs e, string additionalInfo)
    {
        MessageBox.Show(additionalInfo);
        button1.Click -= myEventHandler;
    }
}