In C#, why can't I test if a event handler is null anywhere outside of the class that it's defined?

对着背影说爱祢 提交于 2019-12-03 18:32:36

问题


I am sure that I am just not understanding something fundamental about events and/or delegates in C#, but why can't I do the Boolean tests in this code sample:

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) { }
}

回答1:


An event is really just an "add" operation and a "remove" operation. You can't get the value, you can't set the value, you can't call it - you can just subscribe a handler for the event (add) or unsubscribe one (remove). This is fine - it's encapsulation, plain and simple. It's up to the publisher to implement add/remove appropriately, but unless the publisher chooses to make the details available, subscribers can't modify or access the implementation-specific parts.

Field-like events in C# (where you don't specify the add/remove bits) hide this - they create a variable of a delegate type and an event. The event's add/remove implementations just use the variable to keep track of the subscribers.

Inside the class you refer to the variable (so you can get the currently subscribed delegates, execute them etc) and outside the class you refer to the event itself (so only have add/remove abilities).

The alternative to field-like events is where you explicitly implement the add/remove yourself, e.g.

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;
    }
}

See my article on events for more information.

Of course the event publisher can also make more information available - you could write a property like ClickHandlers to return the current multi-cast delegate, or HasClickHandlersto return whether there are any or not. That's not part of the core event model though.




回答2:


You can easily use a very simple approach here to not repeatedly subscribe to an event.

Either of the 2 approaches below can be used:

  1. Flag approach : _getWarehouseForVendorCompletedSubscribed is a private variable initialized to false.

        if (!_getWarehouseForVendorCompletedSubscribed)
        {
            _serviceClient.GetWarehouseForVendorCompleted += new EventHandler<GetWarehouseForVendorCompletedEventArgs>(_serviceClient_GetWarehouseForVendorCompleted);
            _getWarehouseForVendorCompletedSubscribed = true;
        }
    
  2. Unsubscribe Approach :Include an unsubscribe everytime you want to subscribe.

    _serviceClient.GetWarehouseForVendorCompleted -= new 
        EventHandler<GetWarehouseForVendorCompletedEventArgs>  
     (_serviceClient_GetWarehouseForVendorCompleted);
    
    
    _serviceClient.GetWarehouseForVendorCompleted += new 
           EventHandler<GetWarehouseForVendorCompletedEventArgs>
           (_serviceClient_GetWarehouseForVendorCompleted);
    



回答3:


Here the answer:

using System;
delegate void MyEventHandler();
class MyEvent
{
    string s;
    public event MyEventHandler SomeEvent;
    // This is called to raise the event.
    public void OnSomeEvent()
    {
        if (SomeEvent != null)
        {
            SomeEvent();
        }

    }

    public string IsNull
    {
        get
        {
            if (SomeEvent != null)
                 return s = "The EventHandlerList is not NULL";
            else return s = "The EventHandlerList is NULL"; ;
        }
    }
}

class EventDemo
{  
    // An event handler.
    static void Handler()
    {
       Console.WriteLine("Event occurred");
    }

    static void Main()
    {
       MyEvent evt = new MyEvent();
       // Add Handler() to the event list.
       evt.SomeEvent += Handler;
       // Raise the event.
       //evt.OnSomeEvent();
       evt.SomeEvent -= Handler;
       Console.WriteLine(evt.IsNull);
       Console.ReadKey();
   }
} 



回答4:


Here's a slightly different question

What value is there in testing an externally defined event for null?

As an external consumer of an event you can only do 2 operations

  • Add a handler
  • Remove a handler

The null or non-nullness of the event has no bearing on these 2 actions. Why do you want to run a test which provides no perceivable value?




回答5:


It's a rule in place when using the 'event' keyword. When you create an event, you are restricting outside class interaction with the delegate to a "subscribe / unsubscribe" relationship, this includes cases of inheritance. Remember an event is essentially a property, but for method calls, it isn't really an object itself, so really it looks more like this:

public event SomeEventHandler SomeEvent
{
     add
     {
          //Add method call to delegate
     }
     remove
     {
          //Remove method call to delegate
     }
}



回答6:


You'd have to do that from the base class. That's the exact reason that you did this:

protected void OnSomeEvent(EventArgs e) {
    // CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS.
    if (SomeEvent != null) SomeEvent(this, e);
}

You can't access events from a derived class. Also, you should make that method virtual, so that it can be overridden in a derived class.




回答7:


Publisher of the event implicitly overload only += and -= operations, and other operations are not implemented in the publisher because of the obvious reasons as explained above, such as don't want to give control to subscriber to change events.

If we want to validate if a particular event is subscribed in the subscriber class, better publisher will set a flag in its class when event is subscriber and clear the flag when it is unsubscriber.

If subscriber can access the flag of publisher, very easily identifiable whether the particular event is subscriber or not by checking the flag value.



来源:https://stackoverflow.com/questions/1246116/in-c-why-cant-i-test-if-a-event-handler-is-null-anywhere-outside-of-the-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!