C#事件null检查(来自另一个类)

时间:2011-06-30 03:41:47

标签: c# event-handling

我将lambda表达式设置为事件的事件处理程序

proxy.SomeEvent += (sender, e) => { doSomething(); };

但是在调试时我发现doSomething()被执行了两次,因为上面的事件赋值语句被执行了两次。

所以我想在调用事件赋值语句之前检查proxy.SomeEvent是否为null,如:

if(proxy.SomeEvent == null)
{
    proxy.SomeEvent += (sender, e)=> { doSomething(); };
}

但我收到编译错误事件'Proxy.SomeEvent'只能出现在+ =或 - = 的左侧

由于-=运算符无法删除lambda表达式事件处理程序,是否有其他方法可以检查是否已分配事件?

3 个答案:

答案 0 :(得分:3)

没有(标准)方法来“检查”事件处理程序的内容。

有效表单(用于外部访问)是:

obj.Event += Handler;
obj.Event -= Handler;

当它在类外部访问时,它不允许在Event上调用任何方法,也不支持任何其他运算符。

但是,如果您以保留原始处理程序的方式编写它,那么您可以在事先删除它。

public Handler(object sender, EventArgs) {
    ...
}

// remove if already added (does nothing if it was not added)
// there is no (standard) way to check if it was added so this
// is just a pre-emptive remove
proxy.SomeEvent -= Handler;
// and add the handler
proxy.SomeEvent += Handler;

我不是说这是最好/最好的方法(例如,为什么处理程序允许被多次分配给“同一个”处理程序?),但这是一种方法我有时使用过。

快乐的编码。

答案 1 :(得分:2)

为避免分配语句被执行两次(并处理多个步骤):

class foo {
   bool isAssigned;

   void someMethod()
   {
      if (!isAssigned)
      {
        lock (this)
        {
            if (!isAssigned) proxy.SomeEvent += ...;
            isAssigned = true;
        }
      }
   }

答案 2 :(得分:1)

您的具体问题是如何检查您的lambda是否已经注册,以避免两次注册。在过去,我没有声明一个单独的方法(支持“ - =”),我只是在订阅事件之前将lambda分配给局部变量。

public class SomeOtherClass
{
    public void ResponseToSomeEvent()
    {
        var proxy = new Proxy();

        // Assign the lambda to a local variable
        EventHandler doSomething = (sender, e) => Console.WriteLine("Just Once");

        // Subscribe to event
        proxy.SomeEvent += doSomething;

        proxy.Raise();

        // Unsubscribe and resubscribe to event
        proxy.SomeEvent -= doSomething;
        proxy.SomeEvent += doSomething;

        proxy.Raise();
    }
}

public class Proxy
{
    public event EventHandler SomeEvent;

    public void Raise()
    {
        Console.WriteLine("Raise");
        SomeEvent(this, EventArgs.Empty);
    }
}

结果如预期的那样,lambda仅在每次引发事件时调用一次,因为 - =实际上能够删除订阅,因为您能够提供要删除的事件处理程序。

但是,在更复杂的场景中,您可能正在使用lambda来执行闭包,并且由于代码路径,您无法轻松维护对用于订阅事件的原始lambda的引用。如果您的情况很复杂,我建议创建一个具体的闭包类(类似于C#编译器所做的)并使用闭包类实例上的方法订阅该事件。然后,您需要在闭包类上重写equals,以根据闭包输入值确定事件订阅是否相同。这允许您安全地订阅闭包类的一个实例,并在稍后的某个时候使用不同的实例取消订阅/重新订阅。