webentwicklung-frage-antwort-db.com.de

Warum kann ich in C # nicht testen, ob ein Ereignishandler außerhalb der von ihm definierten Klasse null ist?

Ich bin mir sicher, dass ich etwas Grundlegendes zu Ereignissen und/oder Delegaten in C # nicht verstehe, aber warum kann ich die Booleschen Tests in diesem Codebeispiel nicht durchführen:

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

Ein Ereignis ist wirklich nur eine "Hinzufügen" -Operation und eine "Entfernen" -Operation. Sie können den Wert nicht abrufen, Sie können den Wert nicht festlegen, Sie können ihn nicht aufrufen. Sie können einfach einen Handler für das Ereignis (add) abonnieren oder einen (remove) Abonnement abbestellen. Das ist in Ordnung - es ist einfach und einfach gekapselt. Es ist Sache des Herausgebers, das Hinzufügen/Entfernen entsprechend zu implementieren. Wenn der Herausgeber jedoch nicht die Details verfügbar macht, können Abonnenten die implementierungsspezifischen Teile nicht ändern oder darauf zugreifen.

Feldähnliche Ereignisse in C # (wo Sie die Bits zum Hinzufügen/Entfernen nicht angeben), blenden Sie dies aus - sie erstellen eine Variable eines Delegattyps und ein Ereignis. Die Implementierungs-/Entfernungsimplementierungen des Ereignisses verwenden nur die Variable, um die Abonnenten zu verfolgen.

Innerhalb der Klasse verweisen Sie auf die Variable (damit Sie die aktuell abonnierten Delegaten abrufen können, usw.), und außerhalb der Klasse verweisen Sie auf das Ereignis selbst (also nur über die Fähigkeiten zum Hinzufügen/Entfernen).

Die Alternative zu feldartigen Ereignissen ist, wenn Sie das Hinzufügen/Entfernen explizit selbst implementieren, z.

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

Siehe mein Artikel zu Veranstaltungen für weitere Informationen.

Natürlich kann der Event-Publisher can auch weitere Informationen zur Verfügung stellen - Sie könnten eine Eigenschaft wie ClickHandlers schreiben, um den aktuellen Multicast-Delegierten zurückzugeben, oder HasClickHandlers, um zurückzugeben, ob es welche gibt oder nicht. Das gehört jedoch nicht zum Kernereignismodell.

58
Jon Skeet

Sie können hier ganz einfach einen sehr einfachen Ansatz verwenden, um ein Ereignis nicht wiederholt zu abonnieren.

Jeder der beiden folgenden Ansätze kann verwendet werden:

  1. Flag-Ansatz : _getWarehouseForVendorCompletedSubscribed ist eine private Variable, die mit false initialisiert wird.

        if (!_getWarehouseForVendorCompletedSubscribed)
        {
            _serviceClient.GetWarehouseForVendorCompleted += new EventHandler<GetWarehouseForVendorCompletedEventArgs>(_serviceClient_GetWarehouseForVendorCompleted);
            _getWarehouseForVendorCompletedSubscribed = true;
        }
    
  2. Abo abbestellen : Fügen Sie bei jedem Abonnement ein Abbestellen bei.

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

Hier die Antwort: 

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

Hier ist eine etwas andere Frage

Welchen Wert hat es, ein extern definiertes Ereignis auf null zu testen?

Als externer Verbraucher eines Ereignisses können Sie nur zwei Vorgänge ausführen

  • Fügen Sie einen Handler hinzu
  • Entfernen Sie einen Handler

Die Null- oder Nicht-Nullheit des Ereignisses hat keinen Einfluss auf diese beiden Aktionen. Warum möchten Sie einen Test durchführen, der keinen wahrnehmbaren Wert liefert?

2
JaredPar

Dies ist eine Regel, wenn das Schlüsselwort 'event' verwendet wird. Wenn Sie ein Ereignis erstellen, beschränken Sie die Interaktion außerhalb der Klasse mit dem Delegaten auf eine "Abonnieren/Abbestellen" - Beziehung. Dies schließt Fälle der Vererbung ein. Denken Sie daran, dass ein Ereignis im Wesentlichen eine Eigenschaft ist. Bei Methodenaufrufen ist es jedoch nicht wirklich ein Objekt selbst. Es sieht also eher so aus:

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

Der Herausgeber des Ereignisses überlädt implizit nur +=- und -=-Vorgänge, und andere Vorgänge sind aus den oben beschriebenen offensichtlichen Gründen im Publisher nicht implementiert, z. B. weil der Abonnent nicht die Kontrolle über das Ändern von Ereignissen erhalten möchte.

Wenn Sie überprüfen möchten, ob ein bestimmtes Ereignis in der Abonnentenklasse abonniert ist, wird ein besserer Herausgeber in seiner Klasse ein Flag setzen, wenn das Ereignis ein Abonnent ist, und das Flag löschen, wenn es kein Abonnent ist.

Wenn der Abonnent auf das Flag des Herausgebers zugreifen kann, kann sehr leicht identifiziert werden, ob das bestimmte Ereignis ein Abonnent ist oder nicht, indem der Flagwert geprüft wird.

0
user3202543

Das müsste man von der Basisklasse aus machen. Aus genau diesem Grund haben Sie das getan:

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

Sie können nicht auf Ereignisse von einer abgeleiteten Klasse zugreifen. Sie sollten diese Methode auch als virtuell definieren, damit sie in einer abgeleiteten Klasse überschrieben werden kann.

0
Darthg8r