在C#中,为什么我不能测试事件处理程序在它定义的类之外的任何地方是否为null?

gab*_*abe 34 c# events delegates event-handling

我确信我只是不了解C#中事件和/或委托的基本内容,但为什么我不能在此代码示例中进行布尔测试:

public class UseSomeEventBase {
    public delegate void SomeEventHandler(object sender, EventArgs e);
    public event SomeEventHandler SomeEvent;
    protected void OnSomeEvent(EventArgs e) {
        // CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS.
        if (SomeEvent != null) SomeEvent(this, e);
    }
}

public class UseSomeEvent : UseSomeEventBase {
    public bool IsSomeEventHandlerNull() {
        // "LEFT HAND SIDE" COMPILER ERROR
        return SomeEvent == null;
    }
}

class Program {
    static void Main(string[] args) {
        var useSomeEvent = new UseSomeEvent();
        useSomeEvent.SomeEvent +=new UseSomeEventBase.SomeEventHandler(FuncToHandle);
        // "LEFT HAND SIDE" COMPILER ERROR
        if (useSomeEvent.SomeEvent == null) {

        }
        var useSomeEventBase = new UseSomeEventBase();
        useSomeEventBase.SomeEvent += new UseSomeEventBase.SomeEventHandler(FuncToHandle);
        // "LEFT HAND SIDE" COMPILER ERROR
        if (useSomeEventBase.SomeEvent == null) {

        }
    }

    static void FuncToHandle(object sender, EventArgs e) { }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 58

事件实际上只是一个"添加"操作和一个"删除"操作.您无法获取值,您无法设置该值,您无法调用它 - 您只需为事件(add)订阅处理程序或取消订阅一个(remove).这很好 - 它的封装,简单明了.由发布者适当地实施添加/删除,但除非发布者选择提供详细信息,否则订阅者无法修改或访问特定于实现的部分.

C#中的字段式事件(您没有指定添加/删除位)隐藏了这一点 - 它们创建委托类型事件的变量.事件的添加/删除实现只是使用该变量来跟踪订阅者.

在类中你引用变量(所以你可以获得当前订阅的委托,执行它们等),在类之外你引用事件本身(所以只有添加/删除功能).

类似字段事件的替代方法是您自己显式实现添加/删除,例如

private EventHandler clickHandler; // Normal private field

public event EventHandler Click
{
    add
    {
        Console.WriteLine("New subscriber");
        clickHandler += value;
    }
    remove
    {
        Console.WriteLine("Lost a subscriber");
        clickHandler -= value;
    }
}
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅我关于事件的文章

当然,事件发布也可以提供更多信息 - 您可以编写一个属性,例如ClickHandlers返回当前的多播委托,或者HasClickHandlers返回是否存在.但这并不是核心事件模型的一部分.


Sun*_*nil 16

您可以在此处轻松使用非常简单的方法来重复订阅事件.

可以使用以下两种方法中的任何一种:

  1. 标记方法:_getWarehouseForVendorCompletedSubscribed是一个初始化为false的私有变量.

        if (!_getWarehouseForVendorCompletedSubscribed)
        {
            _serviceClient.GetWarehouseForVendorCompleted += new EventHandler<GetWarehouseForVendorCompletedEventArgs>(_serviceClient_GetWarehouseForVendorCompleted);
            _getWarehouseForVendorCompletedSubscribed = true;
        }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 取消订阅方法:每次订阅时都包含取消订阅.

    _serviceClient.GetWarehouseForVendorCompleted -= new 
        EventHandler<GetWarehouseForVendorCompletedEventArgs>  
     (_serviceClient_GetWarehouseForVendorCompleted);
    
    
    _serviceClient.GetWarehouseForVendorCompleted += new 
           EventHandler<GetWarehouseForVendorCompletedEventArgs>
           (_serviceClient_GetWarehouseForVendorCompleted);
    
    Run Code Online (Sandbox Code Playgroud)

  • 如果尚未添加,则只会忽略 - = (3认同)