Zdarzenia (C++/CX)
Typ środowisko wykonawcze systemu Windows może deklarować (czyli publikować) zdarzenia, a kod klienta w tym samym składniku lub w innych składnikach może subskrybować te zdarzenia, kojarząc metody nazywane procedurami obsługi zdarzeń z zdarzeniem. Z jednym zdarzeniem można skojarzyć wiele programów obsługi zdarzeń. Gdy obiekt publikowania zgłasza zdarzenie, powoduje wywołanie wszystkich programów obsługi zdarzeń. W ten sposób subskrybowana klasa może wykonać dowolną akcję niestandardową, gdy wydawca zgłasza zdarzenie. Zdarzenie ma typ delegata, który określa podpis, który musi mieć wszystkie programy obsługi zdarzeń w celu zasubskrybowania zdarzenia.
Korzystanie ze zdarzeń w składnikach systemu Windows
Wiele składników w środowisko wykonawcze systemu Windows uwidacznia zdarzenia. Na przykład obiekt LightSensor uruchamia zdarzenie ReadingChanged, gdy czujnik zgłasza nową wartość luminascencencji. W przypadku korzystania z obiektu LightSensor w programie można zdefiniować metodę, która będzie wywoływana po uruchomieniu zdarzenia ReadingChanged. Metoda może zrobić to, co chcesz zrobić; jedynym wymaganiem jest to, że jego podpis musi być zgodny z podpisem delegata, który jest wywoływany. Aby uzyskać więcej informacji na temat tworzenia procedury obsługi zdarzeń delegata i subskrybowania zdarzenia, zobacz Delegaty.
Tworzenie zdarzeń niestandardowych
Deklaracja
Zdarzenie można zadeklarować w klasie ref lub interfejsie i może mieć publiczny, wewnętrzny (publiczny/prywatny), chroniony publiczny, chroniony, chroniony, prywatny lub prywatny dostęp. Podczas deklarowania zdarzenia kompilator wewnętrznie tworzy obiekt, który uwidacznia dwie metody dostępu: dodawanie i usuwanie. Podczas subskrybowania obiektów rejestrują programy obsługi zdarzeń, obiekt zdarzenia przechowuje je w kolekcji. Gdy zdarzenie zostanie wyzwolone, obiekt zdarzenia wywołuje wszystkie programy obsługi na liście z kolei. Zdarzenie trywialne — podobnie jak w poniższym przykładzie — ma niejawny magazyn zapasowy, a także metody niejawne add
i remove
metody dostępu. Możesz również określić własne metody dostępu w taki sam sposób, jak można określić niestandardowe get
i set
akcesory na właściwości. Klasa implementowania nie może ręcznie przechodzić przez listę subskrybentów zdarzeń w trywialnym zdarzeniu.
W poniższym przykładzie pokazano, jak zadeklarować i uruchomić zdarzenie. Zwróć uwagę, że zdarzenie ma typ delegata i jest zadeklarowane przy użyciu symbolu "^".
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.");
}
};
}
Użycie
W poniższym przykładzie pokazano, w jaki sposób subskrybowanie klasy używa +=
operatora do subskrybowania zdarzenia i zapewnienia procedury obsługi zdarzeń, która ma zostać wywołana po uruchomieniu zdarzenia. Zwróć uwagę, że podana funkcja jest zgodna z podpisem delegata zdefiniowanego po stronie wydawcy w EventTest
przestrzeni nazw.
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;
};
}
Ostrzeżenie
Ogólnie rzecz biorąc, lepiej jest użyć nazwanej funkcji, a nie lambda, w przypadku obsługi zdarzeń, chyba że zadbasz o to, aby uniknąć odwołań cyklicznych. Nazwana funkcja przechwytuje wskaźnik "this" przez słabe odwołanie, podczas gdy lambda przechwytuje go przez silne odwołanie i tworzy odwołanie cykliczne. Aby uzyskać więcej informacji, zobacz Słabe odwołania i cykle przerwania (C++/CX).
Niestandardowe metody dodawania i usuwania
Wewnętrznie zdarzenie ma metodę dodawania, metodę remove i metodę raise. Gdy kod klienta subskrybuje zdarzenie, wywoływana jest metoda dodawania i delegat przekazany do listy wywołań zdarzenia. Klasa publikowania wywołuje zdarzenie, powoduje wywołanie metody raise(), a każdy delegat na liście jest wywoływany z kolei. Subskrybent może usunąć się z listy delegatów, co powoduje wywołanie metody usuwania zdarzenia. Kompilator udostępnia domyślne wersje tych metod, jeśli nie zdefiniujesz ich w kodzie; są one nazywane zdarzeniami trywialnymi. W wielu przypadkach jest to wszystko, co jest wymagane.
Możesz określić niestandardowe metody dodawania, usuwania i zgłaszania dla zdarzenia, jeśli musisz wykonać logikę niestandardową w odpowiedzi na dodanie lub usunięcie subskrybentów. Jeśli na przykład masz kosztowny obiekt, który jest wymagany tylko do raportowania zdarzeń, możesz z opóźnieniem odroczyć tworzenie obiektu, dopóki klient rzeczywiście zasubskrybuje zdarzenie.
W następnym przykładzie pokazano, jak dodać niestandardowe metody dodawania, usuwania i zgłaszania do zdarzenia:
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);
}
}
};
}
Usuwanie programu obsługi zdarzeń po stronie subskrybenta
W niektórych rzadkich przypadkach możesz usunąć program obsługi zdarzeń dla zdarzenia, do którego wcześniej subskrybowano. Możesz na przykład zastąpić go innym programem obsługi zdarzeń lub usunąć niektóre zasoby, które są przez nią przechowywane. Aby usunąć procedurę obsługi, należy zapisać zdarzenie EventRegistrationToken zwrócone z +=
operacji. Następnie możesz użyć -=
operatora na tokenie, aby usunąć procedurę obsługi zdarzeń. Jednak oryginalny program obsługi może być nadal wywoływany nawet po jego usunięciu. Na przykład może wystąpić warunek wyścigu, gdy źródło zdarzeń pobiera listę procedur obsługi i zaczyna je wywoływać. Jeśli program obsługi zdarzeń zostanie usunięty, gdy tak się stanie, lista stanie się nieaktualna. Jeśli więc zamierzasz usunąć procedurę obsługi zdarzeń, utwórz flagę elementu członkowskiego. Ustaw je, jeśli zdarzenie zostanie usunięte, a następnie w procedurze obsługi zdarzeń sprawdź flagę i zwróć ją natychmiast, jeśli jest ustawiona. W następnym przykładzie przedstawiono podstawowy wzorzec.
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;
}
};
}
Uwagi
Wiele programów obsługi może być skojarzonych z tym samym zdarzeniem. Źródło zdarzeń sekwencyjnie wywołuje wszystkie programy obsługi zdarzeń z tego samego wątku. Jeśli odbiornik zdarzeń blokuje metodę obsługi zdarzeń, blokuje źródło zdarzeń podczas wywoływania innych programów obsługi zdarzeń dla tego zdarzenia.
Kolejność, w jakiej źródło zdarzeń wywołuje programy obsługi zdarzeń w odbiornikach zdarzeń, nie jest gwarantowana i może się różnić od wywołania do wywołania.
Zobacz też
System typów
Delegaci
Dokumentacja języka C++/CX
Dokumentacja przestrzeni nazw