Delegaty (C++/CX)

delegate Słowo kluczowe jest używane do deklarowania typu referencyjnego, który jest odpowiednikiem środowisko wykonawcze systemu Windows obiektu funkcji w standardowym języku C++. Deklaracja delegata podobna do sygnatury funkcji; Określa typ zwracany i typy parametrów, które musi zawierać funkcja opakowana. Jest to zdefiniowana przez użytkownika deklaracja delegata:

public delegate void PrimeFoundHandler(int result);

Delegaty są najczęściej używane w połączeniu z zdarzeniami. Zdarzenie ma typ delegata, tak samo, jak Klasa może mieć typ interfejsu. Delegat reprezentuje kontrakt, który jest bardzo spełniający procedury obsługi zdarzeń. Oto element członkowski klasy zdarzeń, którego typem jest zdefiniowany wcześniej delegat:

event PrimeFoundHandler^ primeFoundEvent;

Podczas deklarowania delegatów, którzy będą narażeni na klientów za pośrednictwem interfejsu binarnego aplikacji środowisko wykonawcze systemu Windows, użyj systemu Windows: <TSender, TResult> : Foundation:: TypedEventHandler. Ten delegat ma wstępnie zdefiniowane pliki binarne proxy i zastępcze, które umożliwiają korzystanie z nich przez klientów JavaScript.

Zużywanie delegatów

Podczas tworzenia aplikacji platforma uniwersalna systemu Windows często pracujesz z delegatem jako typem zdarzenia, które uwidacznia Klasa środowisko wykonawcze systemu Windows. Aby subskrybować zdarzenie, Utwórz wystąpienie jego typu delegata, określając funkcję — lub wyrażenie lambda, które pasuje do sygnatury delegata. Następnie użyj += operatora, aby przekazać obiekt delegata do elementu członkowskiego zdarzenia w klasie. Jest to tzw. subskrybowanie zdarzenia. Gdy wystąpienie klasy "wyzwala" zdarzenie, wywoływana jest funkcja wraz z innymi dodziałami, które zostały dodane przez obiekt lub inne obiekty.

Porada

Program Visual Studio wykonuje dużą nakład pracy podczas tworzenia programu obsługi zdarzeń. Na przykład jeśli określisz procedurę obsługi zdarzeń w znaczniku XAML, zostanie wyświetlona etykietka narzędzia. W przypadku wybrania etykietki narzędzia program Visual Studio automatycznie tworzy metodę obsługi zdarzeń i kojarzy ją ze zdarzeniem klasy Publishing.

Poniższy przykład pokazuje wzorzec podstawowy. Windows::Foundation::TypedEventHandler jest typem delegata. Funkcja obsługi jest tworzona za pomocą nazwanej funkcji.

W aplikacji App. h:

[Windows::Foundation::Metadata::WebHostHiddenAttribute]
ref class App sealed
{        
    void InitializeSensor();
    void SensorReadingEventHandler(Windows::Devices::Sensors::LightSensor^ sender, 
        Windows::Devices::Sensors::LightSensorReadingChangedEventArgs^ args);

    float m_oldReading;
    Windows::Devices::Sensors::LightSensor^ m_sensor;

};

W aplikacji App. cpp:

void App::InitializeSensor()
{
    // using namespace Windows::Devices::Sensors;
    // using namespace Windows::Foundation;
    m_sensor = LightSensor::GetDefault();

    // Create the event handler delegate and add 
    // it  to the object's  event handler list.
    m_sensor->ReadingChanged += ref new  TypedEventHandler<LightSensor^, 
        LightSensorReadingChangedEventArgs^>( this, 
        &App::SensorReadingEventHandler);

}

void App::SensorReadingEventHandler(LightSensor^ sender, 
                                    LightSensorReadingChangedEventArgs^ args)
{    
    LightSensorReading^ reading = args->Reading;
    if (reading->IlluminanceInLux > m_oldReading)
    {/*...*/}

}

Ostrzeżenie

Ogólnie rzecz biorąc, w przypadku programu obsługi zdarzeń lepiej jest używać nazwanej funkcji zamiast wyrażenia lambda, chyba że chcesz uniknąć cyklicznych odwołań. Nazwana funkcja przechwytuje wskaźnik "This" przez słabe odwołanie, ale lambda przechwytuje je przez silną referencję i tworzy odwołanie cykliczne. Aby uzyskać więcej informacji, zobacz słabe odwołania i cykle przerywania.

Według Konwencji nazwy delegatów obsługi zdarzeń zdefiniowane przez środowisko wykonawcze systemu Windows mają postać * EventHandler — na przykład RoutedEventHandler, SizeChangedEventHandler lub SuspendingEventHandler. Ponadto według Konwencji delegatów obsługi zdarzeń ma dwa parametry i zwracają typ void. W delegatze, który nie ma parametrów typu, pierwszy parametr jest typu platform:: Object ^; Przechowuje odwołanie do nadawcy, który jest obiektem, który wygenerował zdarzenie. Przed użyciem argumentu w metodzie obsługi zdarzeń należy wykonać rzutowanie na oryginalny typ. W delegatze obsługi zdarzeń, który ma parametry typu, pierwszy parametr typu określa typ nadawcy, a drugi parametr jest dojściem do klasy referencyjnej, która zawiera informacje o zdarzeniu. Według Konwencji Ta klasa ma nazwę * EventArgs. Na przykład delegat RoutedEventHandler ma drugi parametr typu RoutedEventArgs ^, a DragEventHander ma drugi parametr typu DragEventArgs ^.

Zgodnie z Konwencją Delegaty zawijają kod, który jest wykonywany po zakończeniu operacji asynchronicznej o nazwie * CompletedHandler. Te Delegaty są zdefiniowane jako właściwości w klasie, a nie jako zdarzenia. W związku z tym nie należy używać += operatora, aby subskrybować te elementy; wystarczy przypisać obiekt delegata do właściwości.

Porada

Funkcja IntelliSense języka C++ nie pokazuje pełnego podpisu delegata; w związku z tym nie pomaga określić określonego typu parametru EventArgs. Aby znaleźć typ, możesz przejść do Przeglądarka obiektów i przyjrzeć się Invoke metodzie delegata.

Tworzenie niestandardowych delegatów

Możesz zdefiniować własnych delegatów, aby zdefiniować programy obsługi zdarzeń lub umożliwić użytkownikom przekazywanie funkcji niestandardowych do składnika środowisko wykonawcze systemu Windows. Podobnie jak w przypadku dowolnego innego typu środowisko wykonawcze systemu Windows delegat publiczny nie może być zadeklarowany jako generyczny.

Oświadczeń

Deklaracja delegata jest podobna do deklaracji funkcji, z tą różnicą, że delegat jest typem. Zwykle deklaruje delegata w zakresie przestrzeni nazw, chociaż można także zagnieżdżać deklarację delegata w deklaracji klasy. Następujący delegat hermetyzuje każdą funkcję, która przyjmuje ContactInfo^ jako dane wejściowe i zwraca Platform::String^ .

public delegate Platform::String^ CustomStringDelegate(ContactInfo^ ci);

Po zadeklarowaniu typu delegata można zadeklarować składowe klasy tego typu lub metod, które pobierają obiekty tego typu jako parametry. Metoda lub funkcja może również zwracać typ delegata. W poniższym przykładzie ToCustomString Metoda przyjmuje delegat jako parametr wejściowy. Metoda umożliwia kodowi klienta dostarczanie funkcji niestandardowej, która konstruuje ciąg z niektórych lub wszystkich właściwości publicznych ContactInfo obiektu.

public ref class ContactInfo sealed
{        
public:
    ContactInfo(){}
    ContactInfo(Platform::String^ saluation, Platform::String^ last, Platform::String^ first, Platform::String^ address1);
    property Platform::String^ Salutation;
    property Platform::String^ LastName;
    property Platform::String^ FirstName;
    property Platform::String^ Address1;
    //...other properties

    Platform::String^ ToCustomString(CustomStringDelegate^ func)
    {
        return func(this);
    }       
};

Uwaga

Symbol "^" jest używany podczas odwoływania się do typu delegata, podobnie jak w przypadku dowolnego środowisko wykonawcze systemu Windows typu odwołania.

Deklaracja zdarzenia zawsze ma typ delegata. Ten przykład przedstawia typowy podpis typu delegata w środowisko wykonawcze systemu Windows:

public delegate void RoutedEventHandler(
    Platform::Object^ sender, 
    Windows::UI::Xaml::RoutedEventArgs^ e
    );

ClickZdarzenie w Windows:: UI::Xaml::Controls::Primitives::ButtonBase klasie jest typu RoutedEventHandler . Aby uzyskać więcej informacji, zobacz zdarzenia.

Kod klienta najpierw Konstruuje wystąpienie delegata przy użyciu ref new i dostarczając wyrażenie lambda, które jest zgodne z sygnaturą delegata i definiuje zachowanie niestandardowe.

CustomStringDelegate^ func = ref new CustomStringDelegate([] (ContactInfo^ c)
{
    return c->FirstName + " " + c->LastName;
});

Następnie wywołuje funkcję członkowską i przekazuje delegata. Załóżmy, że ci jest ContactInfo^ wystąpieniem i textBlock jest XAML TextBlock^ .

textBlock->Text = ci->ToCustomString( func );

W następnym przykładzie aplikacja kliencka przekazuje niestandardowego delegata do metody publicznej w składniku środowisko wykonawcze systemu Windows, który wykonuje delegata dla każdego elementu w Vector :

//Client app
obj = ref new DelegatesEvents::Class1();

CustomStringDelegate^ myDel = ref new CustomStringDelegate([] (ContactInfo^ c)
{
    return c->Salutation + " " + c->LastName;
});
IVector<String^>^ mycontacts = obj->GetCustomContactStrings(myDel);
std::for_each(begin(mycontacts), end(mycontacts), [this] (String^ s)
{
    this->ContactString->Text += s + " ";
});
// Public method in WinRT component.
IVector<String^>^ Class1::GetCustomContactStrings(CustomStringDelegate^ del)
{
    namespace WFC = Windows::Foundation::Collections;

    Vector<String^>^ contacts = ref new Vector<String^>();
    VectorIterator<ContactInfo^> i = WFC::begin(m_contacts);
    std::for_each( i ,WFC::end(m_contacts), [contacts, del](ContactInfo^ ci)
    {
        contacts->Append(del(ci));
    });

    return contacts;
}

Budownictwo

Delegat można skonstruować z dowolnego z następujących obiektów:

  • lambda

  • funkcja statyczna

  • wskaźnik do elementu członkowskiego

  • std:: Function

Poniższy przykład pokazuje, jak utworzyć delegata z każdego z tych obiektów. Delegat jest używany w taki sam sposób, niezależnie od typu obiektu, który służy do konstruowania go.


ContactInfo^ ci = ref new ContactInfo("Mr.", "Michael", "Jurek", "1234 Compiler Way");

// Lambda. (Avoid capturing "this" or class members.)
CustomStringDelegate^ func = ref new CustomStringDelegate([] (ContactInfo^ c)
{
    return c->Salutation + " " + c->FirstName + " " + c->LastName;
});

// Static function.
// static Platform::String^ GetFirstAndLast(ContactInfo^ info);   
CustomStringDelegate^ func2 = ref new CustomStringDelegate(Class1::GetFirstAndLast);


// Pointer to member.
// Platform::String^ GetSalutationAndLast(ContactInfo^ info)
CustomStringDelegate^ func3 = ref new CustomStringDelegate(this, &DelegatesEvents::Class1::GetSalutationAndLast);

// std::function
std::function<String^ (ContactInfo^)> f = Class1::GetFirstAndLast;
CustomStringDelegate^ func4 = ref new CustomStringDelegate(f);


// Consume the delegates. Output depends on the 
// implementation of the functions you provide.
textBlock->Text  = func(ci); 
textBlock2->Text = func2(ci);
textBlock3->Text = func3(ci);
textBlock4->Text = func4(ci);

Ostrzeżenie

Jeśli używasz wyrażenia lambda, które przechwytuje wskaźnik "This", pamiętaj, aby użyć -= operatora, aby jawnie wyrejestrować ze zdarzenia przed wyjściem z wyrażenia lambda. Aby uzyskać więcej informacji, zobacz zdarzenia.

Delegaci ogólni

Delegaty generyczne w języku C++/CX mają ograniczenia podobne do deklaracji klas ogólnych. Nie mogą być deklarowane jako publiczne. Można zadeklarować prywatny lub wewnętrzny Delegat ogólny i korzystać z niego z języka C++, ale klienci .NET lub JavaScript nie mogą go wykorzystać, ponieważ nie są emitowane do metadanych. winmd. Ten przykład deklaruje delegata generycznego, który może być użyty tylko przez C++:

generic <typename T>
delegate void  MyEventHandler(T p1, T p2);

Następny przykład deklaruje wyspecjalizowane wystąpienie delegata wewnątrz definicji klasy:

MyEventHandler<float>^ myDelegate;

Obiekty delegowane i wątki

Delegat, podobnie jak obiekt Function, zawiera kod, który będzie wykonywany w pewnym czasie w przyszłości. Jeśli kod, który tworzy i przekazuje delegata, a funkcja, która akceptuje i wykonuje delegata, działa w tym samym wątku, wówczas elementy są stosunkowo proste. Jeśli ten wątek jest wątkiem interfejsu użytkownika, delegat może bezpośrednio manipulować obiektami interfejsu użytkownika, takimi jak kontrolki XAML.

Jeśli aplikacja kliencka załaduje składnik środowisko wykonawcze systemu Windows, który działa w Apartament wątkowy i udostępnia delegatowi ten składnik, domyślnie delegat jest wywoływany bezpośrednio w wątku STA. Większość składników środowisko wykonawcze systemu Windows można uruchamiać w WĄTKach lub MTA.

Jeśli kod, który wykonuje delegata jest uruchomiony w innym wątku — na przykład w kontekście obiektu concurrency:: Task — użytkownik jest odpowiedzialny za synchronizację dostępu do udostępnionych danych. Na przykład, jeśli delegat zawiera odwołanie do wektora, a kontrolka XAML odwołuje się do tego samego wektora, należy wykonać kroki w celu uniknięcia zakleszczenia lub sytuacji wyścigu, które mogą wystąpić, gdy zarówno obiekt delegowany, jak i formant XAML próbuje uzyskać dostęp do wektora w tym samym czasie. Należy również zadbać o to, aby delegat nie podejmował prób przechwycenia przez odniesienia lokalne zmienne, które mogą wykraczać poza zakres przed wywołaniem delegata.

Jeśli chcesz, aby utworzony delegat został wywołany w tym samym wątku, w którym został utworzony — na przykład, Jeśli przekażesz go do składnika, który działa w komórce MTA, i chcesz go wywołać w tym samym wątku co twórca, Użyj przeciążenia konstruktora delegata, który przyjmuje drugi CallbackContext parametr. Tego przeciążenia należy używać tylko w delegatach, które mają zarejestrowany serwer proxy/zastępczy; nie wszystkie Delegaty zdefiniowane w systemie Windows. winmd są zarejestrowane.

Jeśli znasz programy obsługi zdarzeń w programie .NET, wiesz, że zalecaną metodą jest wykonanie lokalnej kopii zdarzenia przed jego wyzwoleniem. Pozwala to uniknąć sytuacji wyścigu, w których program obsługi zdarzeń może zostać usunięty tuż przed wywołaniem zdarzenia. Nie jest to konieczne w języku C++/CX, ponieważ w przypadku dodania lub usunięcia obsługi zdarzeń zostanie utworzona nowa lista obsługi. Ponieważ obiekt C++ zwiększa liczbę odwołań na liście programu obsługi przed wywołaniem zdarzenia, gwarantuje to, że wszystkie programy obsługi będą prawidłowe. Oznacza to jednak, że w przypadku usunięcia programu obsługi zdarzeń w wątku zużywania, ten program obsługi może być nadal wywoływany, jeśli obiekt publikacji nadal działa na jego kopii listy, która jest teraz nieaktualna. Obiekt publikacji nie uzyska zaktualizowanej listy do momentu następnego uruchomienia zdarzenia.

Zobacz też

System typów
Dokumentacja języka C++/CX
Dokumentacja przestrzeni nazw