Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/278.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在C#中,为什么可以';测试一个事件处理程序在它所属的类之外的任何地方是否为null';什么是定义?_C#_Events_Event Handling_Delegates - Fatal编程技术网

在C#中,为什么可以';测试一个事件处理程序在它所属的类之外的任何地方是否为null';什么是定义?

在C#中,为什么可以';测试一个事件处理程序在它所属的类之外的任何地方是否为null';什么是定义?,c#,events,event-handling,delegates,C#,Events,Event Handling,Delegates,我确信我只是不了解C#中事件和/或委托的一些基本知识,但为什么我不能在这个代码示例中进行布尔测试: public class UseSomeEventBase { public delegate void SomeEventHandler(object sender, EventArgs e); public event SomeEventHandler SomeEvent; protected void OnSomeEvent(EventArgs e) {

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

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

C#中类似字段的事件(不指定添加/删除位)隐藏了这一点-它们创建委托类型的变量和事件。事件的add/remove实现只是使用变量来跟踪订阅者

在类内部引用变量(因此可以获取当前订阅的委托,执行委托等),在类外部引用事件本身(因此只有添加/删除功能)

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

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;
    }
}
有关更多信息,请参阅


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

您必须从基类执行此操作。这正是你这么做的原因:

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

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

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

测试外部定义的事件是否为null有什么价值

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

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

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

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

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

在这里,您可以轻松地使用一种非常简单的方法来避免重复订阅事件

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

  • 标记方法:\u getWarehouseForVendorCompletedSubscribed是一个初始化为false的私有变量

        if (!_getWarehouseForVendorCompletedSubscribed)
        {
            _serviceClient.GetWarehouseForVendorCompleted += new EventHandler<GetWarehouseForVendorCompletedEventArgs>(_serviceClient_GetWarehouseForVendorCompleted);
            _getWarehouseForVendorCompletedSubscribed = true;
        }
    
    if(!\u获得供应商仓库或完全订阅)
    {
    _serviceClient.GetWarehouseForVendorCompleted+=新事件处理程序(\u serviceClient\u GetWarehouseForVendorCompleted);
    _getWarehouseForVendorCompletedSubscribed=true;
    }
    
  • 取消订阅方法:每次您想要订阅时都包括取消订阅

    _serviceClient.GetWarehouseForVendorCompleted -= new 
        EventHandler<GetWarehouseForVendorCompletedEventArgs>  
     (_serviceClient_GetWarehouseForVendorCompleted);
    
    
    _serviceClient.GetWarehouseForVendorCompleted += new 
           EventHandler<GetWarehouseForVendorCompletedEventArgs>
           (_serviceClient_GetWarehouseForVendorCompleted);
    
    \u serviceClient.getWarehouseforVendor Completed-=新建
    事件处理程序
    (_serviceClient_GetWarehouse for Vendor Completed);
    _serviceClient.GetWarehouseforVendor完成+=新建
    事件处理程序
    (_serviceClient_GetWarehouse for Vendor Completed);
    
  • 答案如下:

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

    事件的发布者仅隐式重载
    +=
    -=
    操作,其他操作未在发布者中实现,原因显而易见,如上所述,例如不希望将更改事件的控制权授予订阅者

    如果我们想验证订阅者类中是否订阅了特定事件,better publisher将在事件为订阅者时在其类中设置一个标志,并在事件为取消订阅者时清除该标志


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

    请注意,这不是线程安全的。如果在测试之后调用之前删除了最后一个处理程序,它可能会抛出NullReferenceException。@Jon-我确实读过你的文章,但我认为这不会发生在这个web应用中。+1是答案。当然,您可以通过内部的多重强制转换委托,将添加/删除操作作为显式方法来实现。在这种情况下,您可以自己跟踪订户和取消订户,从而提供对附加信息的访问(例如订户数量等)-如果您选择。请注意,事件以这种方式实现的原因是任何类都不能通过简单地访问事件(如委托属性)来随意更改具有该事件的类的行为并删除了它的订阅者列表。@womp:我想封装部分和我的链接文章已经涵盖了这一点。将在第一段中添加一句话,以确保。应该是“…公共事件事件处理程序单击…”我正在尝试为2个设计糟糕的第三方web控件编写一个包装工作类,我被要求用于项目(我讨厌这种疯狂的控件)。我试图强制使用特定方法作为类(包装器)的成员对象(引用一个web控件)中事件的事件处理程序。我想在包装器类中进行检查,以确保包装器类的用户已将特定事件处理程序添加到成员对象的事件中。这两个愚蠢的控件非常特殊,很难解释…为什么我们会认为没有可感知的价值?直接价值是信息——它回答了“有订户吗?”。这一信息的价值