代码之家  ›  专栏  ›  技术社区  ›  gabe

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

  •  32
  • gabe  · 技术社区  · 15 年前

    我确信我只是不理解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) { }
    }
    
    7 回复  |  直到 7 年前
        1
  •  58
  •   Jon Skeet    15 年前

    事件实际上只是一个“添加”操作和一个“删除”操作。您不能获取该值,不能设置该值,也不能调用该值-您可以为该事件订阅一个处理程序( 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;
        }
    }
    

    my article on events 更多信息。

    当然是事件发布者 可以 还可以提供更多信息-您可以编写类似 ClickHandlers 返回当前的多播委托,或 HasClickHandlers 返回是否有。但这不是核心事件模型的一部分。

        2
  •  14
  •   Sunil    8 年前

    您可以使用非常简单的方法来避免重复订阅事件。

    可以使用以下两种方法之一:

    1. 旗帜法 :“GetWarehouseForVendorCompletedSubcribed是一个初始化为false的私有变量。

          if (!_getWarehouseForVendorCompletedSubscribed)
          {
              _serviceClient.GetWarehouseForVendorCompleted += new EventHandler<GetWarehouseForVendorCompletedEventArgs>(_serviceClient_GetWarehouseForVendorCompleted);
              _getWarehouseForVendorCompletedSubscribed = true;
          }
      
    2. 取消订阅方法 :每次要订阅时都包括取消订阅。

      _serviceClient.GetWarehouseForVendorCompleted -= new 
          EventHandler<GetWarehouseForVendorCompletedEventArgs>  
       (_serviceClient_GetWarehouseForVendorCompleted);
      
      
      _serviceClient.GetWarehouseForVendorCompleted += new 
             EventHandler<GetWarehouseForVendorCompletedEventArgs>
             (_serviceClient_GetWarehouseForVendorCompleted);
      
        3
  •  3
  •   akjoshi HCP    13 年前

    答案如下:

    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
  •  2
  •   JaredPar    15 年前

    这里有一个稍微不同的问题

    测试外部定义的空事件时有什么值?

    作为事件的外部使用者,您只能执行两个操作

    • 添加处理程序
    • 删除处理程序

    事件的空或非空与这两个操作无关。为什么要运行一个不提供可感知值的测试?

        5
  •  1
  •   Zensar    15 年前

    这是使用“event”关键字时的规则。创建事件时,将与委托的外部类交互限制为“订阅/取消订阅”关系,这包括继承情况。记住,事件本质上是一个属性,但对于方法调用,它实际上不是对象本身,因此实际上它看起来更像这样:

    public event SomeEventHandler SomeEvent
    {
         add
         {
              //Add method call to delegate
         }
         remove
         {
              //Remove method call to delegate
         }
    }
    
        6
  •  0
  •   Matt Grande    15 年前

    你必须从基类那里做。这就是你这么做的确切原因:

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

    无法从派生类访问事件。另外,您应该使该方法成为虚拟的,以便它可以在派生类中被重写。

        7
  •  0
  •   Robert Mars_win    10 年前

    事件的发布者仅隐式重载 += -= 操作和其他操作没有在发布服务器中实现,原因很明显,如上所述,例如不希望向订阅服务器授予更改事件的控制权。

    如果我们想验证订阅服务器类中是否订阅了特定事件,那么当事件是订阅服务器时,更好的发布服务器将在其类中设置一个标志,当事件是订阅服务器时清除该标志。

    如果订阅服务器可以访问发布服务器的标志,则通过检查标志值很容易识别特定事件是否为订阅服务器。