简体   繁体   English

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

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

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: 我确信我只是不了解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) { }
}

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 ). 您无法获取该值,您无法设置该值,也无法调用它-您只能为事件订阅一个处理程序( add )或取消订阅一个事件( 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. C#中的类似字段的事件 (您未指定添加/删除位)将其隐藏起来-它们创建了委托类型的变量事件。 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, eg 类字段事件的替代方法是您自己明确实现添加/删除的位置,例如

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 HasClickHandlers to return whether there are any or not. 当然,事件发布还可以提供更多信息-您可以编写诸如ClickHandlers类的属性以返回当前的多播委托,或HasClickHandlers以返回是否存在。 That's not part of the core event model though. 但是,这不是核心事件模型的一部分。

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: 可以使用以下2种方法之一:

  1. Flag approach : _getWarehouseForVendorCompletedSubscribed is a private variable initialized to false. 标志方法 :_getWarehouseForVendorCompletedSubscribed是初始化为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); 

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

Here's a slightly different question 这是一个稍微不同的问题

What value is there in testing an externally defined event for null? 测试外部定义的事件是否为null有什么值?

As an external consumer of an event you can only do 2 operations 作为事件的外部使用者,您只能执行2个操作

  • Add a handler 添加处理程序
  • Remove a handler 删除处理程序

The null or non-nullness of the event has no bearing on these 2 actions. 事件的null或非null与这两个动作无关。 Why do you want to run a test which provides no perceivable value? 为什么要运行没有可感知值的测试?

It's a rule in place when using the 'event' keyword. 这是使用'event'关键字的规则。 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
     }
}

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. 同样,您应该使该方法成为虚拟方法,以便可以在派生类中重写该方法。

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. 如果订户可以访问发布者的标志,则可以通过检查标志值很容易地确定特定事件是否为订户。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 C# UWP 为什么我不能添加第二个事件处理程序? - C# UWP why can't I add a second event handler? 在c#中的事件处理程序中定义了事件处理程序之外的变量时,如何访问它? - How to access a variable outside an event handler when it is defined within the event handler in c#? 为什么我不能使用反射来获取LinkBut​​ton事件的事件处理程序? - Why can't I get the event handler of a LinkButton's event using reflection? 可以在C#中的任何类之外定义什么? - What can be defined outside any class in C#? 为什么不能在C#中为对象分配null? - Why can't I assign null to an Object in C#? C#:如何在TextBox派生的类中从事件处理程序引发异常? - C#: How can I raise an exception from an event handler in a class derived from TextBox? C#为什么我不能升级到插件的基类? - c# why can't I upcast to my plug-in's base class? 理解 C# 事件处理程序:为什么没有事件? - Understanding C# Event Handler: why no event? 事件处理程序返回空C#? - Event Handler returning null c#? C# - 无法从事件处理程序访问全局变量 - C# - Can't access a global variable from an event handler
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM