Ereignisse (C++/CX)

Ein Windows-Runtime Typ kann Ereignisse deklarieren (d. h. veröffentlichen), und Clientcode in derselben Komponente oder in anderen Komponenten können diese Ereignisse abonnieren, indem Methoden, die als Ereignishandler bezeichnet werden, dem Ereignis zugeordnet werden. Mehrere Ereignishandler können einem einzelnen Ereignis zugeordnet werden. Wenn das Veröffentlichungsobjekt das Ereignis auslöst, werden alle Ereignishandler aufgerufen. Auf diese Weise kann eine abonnierende Klasse eine beliebige geeignete benutzerdefinierte Aktion durchführen, wenn der Herausgeber das Ereignis auslöst. Ein Ereignis hat einen Delegattyp, der die Signatur angibt, die alle Ereignishandler verwenden, um das Ereignis zu abonnieren.

Verwenden von Ereignissen in den Windows-Komponenten

Viele Komponenten im Windows-Runtime Ereignisse verfügbar machen. Beispielsweise löst ein LightSensor-Objekt ein ReadingChanged-Ereignis aus, wenn der Sensor einen neuen Lumineszenzwert meldet. Wenn Sie ein LightSensor-Objekt im Programm verwenden, können Sie eine Methode definieren, die aufgerufen wird, wenn das ReadingChanged-Ereignis ausgelöst wird. Die Methode kann alles tun, was Sie tun möchten; Die einzige Anforderung besteht darin, dass ihre Signatur mit der Signatur des aufgerufenen Delegaten übereinstimmen muss. Weitere Informationen zum Erstellen eines Stellvertretungsereignishandlers und Abonnieren eines Ereignisses finden Sie unter Delegaten.

Erstellen von benutzerdefinierten Ereignissen

Deklaration

Sie können ein Ereignis in einer Verweisklasse oder einer Schnittstelle deklarieren, und der Zugriff darauf kann öffentlich, intern (öffentlich/privat), öffentlich geschützt, geschützt, privat geschützt oder privat sein. Wenn Sie ein Ereignis deklarieren, erstellt der Compiler intern ein Objekt, das zwei Accessormethoden bereitstellt: Hinzufügen und Entfernen. Registrieren Sie beim Abonnieren von Objekten Ereignishandler. Das Ereignisobjekt speichert sie in einer Sammlung. Wenn ein Ereignis ausgelöst wird, ruft das Ereignisobjekt wiederum alle Handler in der Liste auf. Ein triviales Ereignis – wie das im folgenden Beispiel – hat einen impliziten Auslagerungsspeicher sowie implizite add - und remove -Accessormethoden. Sie können auch eigene Accessoren angeben, ebenso wie Sie benutzerdefinierte get - und set -Accessoren für eine Eigenschaft angeben können. Die implementierende Klasse kann die Ereignisabonnentenliste bei einem trivialen Ereignis nicht manuell durchlaufen.

Im folgenden Beispiel werden die Deklaration und das Auslösen einer Schnittstelle erläutert. Beachten Sie, dass das Ereignis über einen Delegattyp verfügt und deklariert wird, indem Sie das "^"-Symbol verwendet wird.

namespace EventTest
{
    ref class Class1;
    public delegate void SomethingHappenedEventHandler(Class1^ sender, Platform::String^ s);

    public ref class Class1 sealed
    {
    public:
        Class1(){}
        event SomethingHappenedEventHandler^ SomethingHappened;
        void DoSomething()
        {
            //Do something....

            // ...then fire the event:
            SomethingHappened(this, L"Something happened.");
        }
    };
}

Verwendung

Im folgenden Beispiel wird gezeigt, wie der += -Operator von einer abonnierenden Klasse verwendet wird, um das Ereignis zu abonnieren, und wie ein Ereignishandler bereitgestellt wird, der ausgeführt wird, wenn das Ereignis ausgelöst wird. Beachten Sie, dass die bereitstellende Funktion der Signatur des Delegaten entspricht, der auf der Herausgeberseite im EventTest -Namespace definiert ist.

namespace EventClient
{
    using namespace EventTest;
    namespace PC = Platform::Collections; //#include <collection.h>

    public ref class Subscriber sealed
    {
    public:
        Subscriber() : eventCount(0) 
        {
            // Instantiate the class that publishes the event.
            publisher= ref new EventTest::Class1();

            // Subscribe to the event and provide a handler function.
            publisher->SomethingHappened += 
                ref new EventTest::SomethingHappenedEventHandler(
                this,
                &Subscriber::MyEventHandler);
            eventLog = ref new PC::Map<int, Platform::String^>();
        }
        void SomeMethod()
        {            
            publisher->DoSomething();
        }

        void MyEventHandler(EventTest::Class1^ mc, Platform::String^ msg)
        {
            // Our custom action: log the event.
            eventLog->Insert(eventCount, msg);
            eventCount++;
        }

    private:
        PC::Map<int, Platform::String^>^ eventLog;
        int eventCount;
        EventTest::Class1^ publisher;
    };
}

Warnung

Im Allgemeinen empfiehlt es sich, eine benannte Funktion statt eines Lambda-Ausdrucks für einen Ereignishandler zu verwenden. Andernfalls müssen Sie sorgfältig darauf achten, Zirkelverweise zu vermeiden. Eine benannte Funktion übernimmt den this-Zeiger als schwachen Verweis, während ein Lambda den this-Zeiger als starken Verweis übernimmt und einen Zirkelverweis erstellt. Weitere Informationen finden Sie unter Weak references and breaking cycles (C++/CX).

Benutzerdefinierte Hinzufügen- und Entfernen-Methoden

Intern verfügt ein Ereignis über eine add-Methode, eine remove-Methode und eine raise-Methode. Wenn Clientcode ein Ereignis abonniert, wird die add-Methode aufgerufen und der übergebene Delegat der Aufrufliste des Ereignisses hinzugefügt. Die Veröffentlichungsklasse ruft das Ereignis auf, dadurch wird die raise()-Methode aufgerufen, und jeder Delegat in der Liste wird wiederum aufgerufen. Ein Abonnent kann sich selbst aus der Delegatliste entfernen, wodurch die remove-Methode des Ereignisses aufgerufen wird. Der Compiler stellt Standardversionen dieser Methoden bereit, wenn Sie sie nicht im Code definieren. Diese werden als triviale Ereignisse bezeichnet. In vielen Fällen ist nur ein triviales Ereignis erforderlich.

Sie können benutzerdefinierte Hinzufügen-, Entfernen- und Auslösen-Methoden für ein Ereignis angeben, wenn Sie benutzerdefinierte Logik als Reaktion auf das Hinzufügen oder Entfernen von Abonnenten ausführen müssen. Wenn Sie beispielsweise über ein teures Objekt verfügen, das nur für die Ereignisberichterstellung erforderlich ist, können Sie die Erstellung des Objekts verzögern, bis ein Client tatsächlich das Ereignis abonniert.

Im nächsten Beispiel wird veranschaulicht, wie Sie benutzerdefinierte Hinzufügen-, Entfernen- und Auslösen-Methoden zu einem Ereignis hinzufügen können.

namespace EventTest2
{
    ref class Class1;
    public delegate void SomethingHappenedEventHandler(Class1^ sender, Platform::String^ msg);

    public ref class Class1 sealed
    {
    public:
        Class1(){}
        event SomethingHappenedEventHandler^ SomethingHappened;
        void DoSomething(){/*...*/}
        void MethodThatFires()
        {
            // Fire before doing something...
            BeforeSomethingHappens(this, "Something's going to happen.");
            
            DoSomething();

            // ...then fire after doing something...
            SomethingHappened(this, L"Something happened.");
        }

        event SomethingHappenedEventHandler^ _InternalHandler;

        event SomethingHappenedEventHandler^ BeforeSomethingHappens
        {
            Windows::Foundation::EventRegistrationToken add(SomethingHappenedEventHandler^ handler)
            {
                // Add custom logic here:
                //....
                return _InternalHandler += handler;
            }

            void remove(Windows::Foundation::EventRegistrationToken token)
            {
                // Add custom logic here:
                //....
                _InternalHandler -= token;
            }

            void raise(Class1^ sender, Platform::String^ str)
            {

                // Add custom logic here:
                //....
                return _InternalHandler(sender, str);
            }
        }
    };
}

Entfernen eines Ereignishandlers von der Abonnentenseite

In einigen wenigen Fällen möchten Sie vielleicht einen Ereignishandler für ein Ereignis entfernen, das Sie zuvor abonniert hatten. Sie möchten es beispielsweise durch einen anderen Ereignishandler ersetzen oder einige Ressourcen löschen, die durch diesen Ereignishandler gehalten werden. Um einen Handler zu entfernen, müssen Sie das von der += -Operation zurückgegebene EventRegistrationToken speichern. Sie können den -= -Operator im Token dann verwenden, um einen Ereignishandler zu entfernen. Allerdings kann der ursprüngliche Handler noch aufgerufen werden, nachdem er entfernt wurde. Eine Racebedingung kann z. B. auftreten, wenn die Ereignisquelle eine Liste von Handlern abruft und sie aufruft. Wenn ein Ereignishandler entfernt wird, während dies geschieht, wird die Liste veraltet. Wenn Sie also einen Ereignishandler entfernen möchten, erstellen Sie ein Memberflaggen. Legen Sie es fest, wenn das Ereignis entfernt wird, und überprüfen Sie dann im Ereignishandler das Flag, und geben Sie sofort zurück, wenn es festgelegt ist. Das grundlegende Muster wird im nächsten Beispiel veranschaulicht.

namespace EventClient2
{
    using namespace EventTest2;

    ref class Subscriber2 sealed
    {
    private:
        bool handlerIsActive; 
        Platform::String^ lastMessage;

        void TestMethod()
        {
            Class1^ c1 = ref new Class1();
            handlerIsActive = true;
            Windows::Foundation::EventRegistrationToken cookie =
                c1->SomethingHappened += 
                ref new EventTest2::SomethingHappenedEventHandler(this, &Subscriber2::MyEventHandler);
            c1->DoSomething();

            // Do some other work�..then remove the event handler and set the flag.
            handlerIsActive = false;
            c1->SomethingHappened -= cookie;           
        }

        void MyEventHandler(Class1^ mc, Platform::String^ msg)
        {
            if (!handlerIsActive)
                return;
            lastMessage = msg;
        }
    };
}

Hinweise

Mehrere Handler können demselben Ereignis zugeordnet werden. Die Ereignisquelle ruft sequenziell alle Ereignishandler von dem gleichen Thread auf. Wenn ein Ereignisempfänger innerhalb der Ereignishandlermethode blockiert, blockiert den Aufruf anderer Ereignishandler für dieses Ereignis durch die Ereignisquelle.

Die Reihenfolge, in der die Ereignisquelle Ereignishandler auf Ereignisempfängern aufruft, wird nicht garantiert und unterscheidet sich von Aufruf zu Aufruf.

Siehe auch

Typsystem
Delegaten
C++-/CX-Programmiersprachenreferenz
Referenz zu Namespaces