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

本文关键字:另一个 事件 null 检查 | 更新日期: 2023-09-27 18:00:39

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

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

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

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

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

但是我得到了一个编译器错误事件"代理"。SomeEvent’只能出现在+=或-=的左侧

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

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

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

只有有效表格(用于外部访问)为:

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

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

但是,如果您以这样一种方式编写它,即保留原始处理程序,那么您可以提前将其删除。

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;

我并不是说这是最好的/好的方法(例如,为什么允许为"同一"处理程序多次分配处理程序?),但这是我偶尔使用的一种方法。

快乐的编码。

为了避免赋值语句被执行两次(并处理多个线程):

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

您的具体问题是如何检查lambda是否已经注册,以避免注册两次。在过去的I中,我没有声明一个单独的方法(它将支持"-="),我只是在订阅事件之前将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,以根据闭包输入值确定事件订阅是否相同。这允许您安全地订阅闭包类的一个实例,并在以后的某个时间点取消订阅/重新订阅另一个实例。