Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Nyckelordet delegate används för att deklarera en referenstyp som är Windows Runtime-motsvarigheten till ett funktionsobjekt i standard-C++. En delegatdeklaration som liknar en funktionssignatur. den anger returtyp och parametertyper som dess omslutna funktion måste ha. Det här är en användardefinierad delegatdeklaration:
public delegate void PrimeFoundHandler(int result);
Ombud används oftast tillsammans med händelser. En händelse har en ombudstyp, på ungefär samma sätt som en klass kan ha en gränssnittstyp. Ombudet representerar ett kontrakt som händelsehanterarna uppfyller. Här är en händelseklassmedlem vars typ är det tidigare definierade ombudet:
event PrimeFoundHandler^ primeFoundEvent;
När du deklarerar ombud som ska exponeras för klienter i windows Runtime-programmets binära gränssnitt använder du Windows::Foundation::TypedEventHandler<TSender, TResult>. Det här ombudet har fördefinierade proxy- och stub-binärfiler som gör att den kan användas av JavaScript-klienter.
Använda ombud
När du skapar en universell Windows-plattformsapp arbetar du ofta med ett ombud som den typ av händelse som en Windows Runtime-klass exponerar. Om du vill prenumerera på en händelse skapar du en instans av dess ombudstyp genom att ange en funktion – eller lambda – som matchar delegatsignaturen. Använd sedan operatorn += för att skicka ombudsobjektet till händelsemedlemmen i klassen. Detta kallas för att prenumerera på händelsen. När klassinstansen "utlöser" händelsen anropas din funktion, tillsammans med andra hanterare som har lagts till av objektet eller andra objekt.
Tips/Råd
Visual Studio gör mycket arbete åt dig när du skapar en händelsehanterare. Om du till exempel anger en händelsehanterare i XAML-markering visas ett verktygstips. Om du väljer verktygstipset skapar Visual Studio automatiskt händelsehanterarmetoden och associerar den med händelsen i publiceringsklassen.
I följande exempel visas det grundläggande mönstret.
Windows::Foundation::TypedEventHandler är ombudstypen. Hanteringsfunktionen skapas med hjälp av en namngiven funktion.
I 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;
};
I 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)
{/*...*/}
}
Varning
För en händelsehanterare är det i allmänhet bättre att använda en namngiven funktion i stället för en lambda om du inte är noga med att undvika cirkelreferenser. En namngiven funktion fångar pekaren "this" med svag referens, men en lambda fångar den med stark referens och skapar en cirkelreferens. Mer information finns i Svaga referenser och icke-bakåtkompatibla cykler.
Enligt konventionen har händelsehanterardelegatnamn som definieras av Windows Runtime formuläret *EventHandler, till exempel RoutedEventHandler, SizeChangedEventHandler eller SuspendingEventHandler. Händelsehanterardelegater har också två parametrar och returnerar tomrum. I ett ombud som inte har typparametrar är den första parametern av typen Plattform::Objekt^; den innehåller en referens till avsändaren, vilket är det objekt som utlöste händelsen. Du måste återställa till den ursprungliga typen innan du använder argumentet i händelsehanterarmetoden. I ett händelsehanterardelegat som har typparametrar anger den första typparametern avsändarens typ och den andra parametern är en referens till en referensklass som innehåller information om händelsen. Enligt konventionen heter den klassen *EventArgs. Ett RoutedEventHandler-ombud har till exempel en andra parameter av typen RoutedEventArgs^, och DragEventHander har en andra parameter av typen DragEventArgs^.
Enligt konventionen får de ombud som omsluter koden som körs när en asynkron åtgärd slutförs namnet *CompletedHandler. Dessa ombud definieras som egenskaper för klassen, inte som händelser. Därför använder du inte operatorn += för att prenumerera på dem. Du tilldelar bara ett ombudsobjekt till egenskapen.
Tips/Råd
C++ IntelliSense visar inte den fullständiga ombudssignaturen. Därför hjälper det dig inte att fastställa den specifika typen av Parametern EventArgs. Om du vill hitta typen kan du gå till Object Browser och titta på Invoke metoden för ombudet.
Skapa anpassade ombud
Du kan definiera dina egna ombud, definiera händelsehanterare eller för att göra det möjligt för konsumenter att skicka in anpassade funktioner till din Windows Runtime-komponent. Precis som andra Windows Runtime-typer kan ett offentligt ombud inte deklareras som generiskt.
Deklaration
Deklarationen av ett ombud liknar en funktionsdeklaration förutom att ombudet är en typ. Vanligtvis deklarerar du ett ombud i namnområdesomfånget, men du kan även kapsla en delegatdeklaration i en klassdeklaration. Följande ombud kapslar in alla funktioner som tar en ContactInfo^ som indata och returnerar en Platform::String^.
public delegate Platform::String^ CustomStringDelegate(ContactInfo^ ci);
När du har deklarerat en ombudstyp kan du deklarera klassmedlemmar av den typen eller metoder som tar objekt av den typen som parametrar. En metod eller funktion kan också returnera en ombudstyp. I följande exempel ToCustomString tar metoden ombudet som en indataparameter. Metoden gör det möjligt för klientkod att tillhandahålla en anpassad funktion som konstruerar en sträng från vissa eller alla offentliga egenskaper för ett ContactInfo objekt.
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);
}
};
Anmärkning
Du använder symbolen ^när du refererar till ombudstypen, precis som med alla Windows Runtime-referenstyper.
En händelsedeklaration har alltid en ombudstyp. Det här exemplet visar en typisk signatur för ombudstyp i Windows Runtime:
public delegate void RoutedEventHandler(
Platform::Object^ sender,
Windows::UI::Xaml::RoutedEventArgs^ e
);
Händelsen Click i Windows:: UI::Xaml::Controls::Primitives::ButtonBase klassen är av typen RoutedEventHandler. Mer information finns i Händelser.
Klientkoden konstruerar först ombudsinstansen med hjälp ref new av och tillhandahåller en lambda som är kompatibel med ombudets signatur och definierar det anpassade beteendet.
CustomStringDelegate^ func = ref new CustomStringDelegate([] (ContactInfo^ c)
{
return c->FirstName + " " + c->LastName;
});
Den anropar sedan medlemsfunktionen och skickar ombudet. Anta att är ci en ContactInfo^ instans och textBlock är en XAML TextBlock^.
textBlock->Text = ci->ToCustomString( func );
I nästa exempel skickar en klientapp ett anpassat ombud till en offentlig metod i en Windows Runtime-komponent som kör ombudet mot varje objekt i en 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;
}
Byggnation
Du kan skapa ett ombud från något av följande objekt:
lambda
statisk funktion
pekare till medlem
std::function
I följande exempel visas hur du konstruerar ett ombud från vart och ett av dessa objekt. Du använder ombudet på exakt samma sätt oavsett vilken typ av objekt som används för att konstruera det.
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);
Varning
Om du använder en lambda som samlar in pekaren "den här" ska du använda operatorn -= för att uttryckligen avregistrera från händelsen innan du avslutar lambda. Mer information finns i Händelser.
Allmänna ombud
Allmänna ombud i C++/CX har begränsningar som liknar deklarationer av generiska klasser. De kan inte deklareras som offentliga. Du kan deklarera ett privat eller internt allmänt ombud och använda det från C++, men .NET- eller JavaScript-klienter kan inte använda det eftersom det inte skickas till winmd-metadata. I det här exemplet deklareras ett allmänt ombud som bara kan användas av C++:
generic <typename T>
delegate void MyEventHandler(T p1, T p2);
I nästa exempel deklareras en specialiserad instans av ombudet i en klassdefinition:
MyEventHandler<float>^ myDelegate;
Ombud och trådar
Ett ombud, precis som ett funktionsobjekt, innehåller kod som kommer att köras någon gång i framtiden. Om koden som skapar och skickar ombudet och funktionen som accepterar och kör ombudet körs i samma tråd är det relativt enkelt. Om tråden är användargränssnittstråden kan ombudet direkt ändra användargränssnittsobjekt, till exempel XAML-kontroller.
Om en klientapp läser in en Windows Runtime-komponent som körs i en trådad lägenhet och tillhandahåller ett ombud för komponenten anropas ombudet som standard direkt i STA-tråden. De flesta Windows Runtime-komponenter kan köras i ANTINGEN STA eller MTA.
Om koden som kör ombudet körs på en annan tråd, till exempel inom ramen för en samtidighet::aktivitetsobjekt, ansvarar du för att synkronisera åtkomsten till delade data. Om ombudet till exempel innehåller en referens till en vektor och en XAML-kontroll har en referens till samma vektor, måste du vidta åtgärder för att undvika dödlägen eller konkurrensförhållanden som kan inträffa när både ombudet och XAML-kontrollen försöker komma åt vektorn samtidigt. Du måste också se till att ombudet inte försöker samla in med lokala referensvariabler som kan gå utanför omfånget innan ombudet anropas.
Om du vill att det skapade ombudet ska anropas tillbaka på samma tråd som det skapades på, till exempel om du skickar den till en komponent som körs i en MTA-lägenhet, och du vill att den ska anropas i samma tråd som skaparen, använder du överlagringen av ombudskonstruktorn som tar en andra CallbackContext parameter. Använd endast den här överbelastningen på ombud som har en registrerad proxy/stub. Inte alla ombud som definieras i Windows.winmd är registrerade.
Om du är bekant med händelsehanterare i .NET vet du att den rekommenderade metoden är att göra en lokal kopia av en händelse innan du utlöses. Detta förhindrar tävlingsförhållanden där en händelsehanterare kan tas bort precis innan händelsen anropas. Det är inte nödvändigt att göra detta i C++/CX eftersom när händelsehanterare läggs till eller tas bort skapas en ny hanterarlista. Eftersom ett C++-objekt ökar referensantalet i hanteringslistan innan en händelse anropas, är det garanterat att alla hanterare är giltiga. Men det innebär också att om du tar bort en händelsehanterare i den förbrukande tråden kan den hanteraren fortfarande anropas om publiceringsobjektet fortfarande körs på dess kopia av listan, som nu är inaktuell. Publiceringsobjektet hämtar inte den uppdaterade listan förrän nästa gång händelsen utlöses.
Se även
typsystem
C++/CX-språkreferens
referens för namnområden