Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
A delegate palavra-chave é usada para declarar um tipo de referência que é o equivalente do Tempo de Execução do Windows de um objeto de função em C++ padrão. Uma declaração de delegado semelhante a uma assinatura de função; ele especifica o tipo de retorno e os tipos de parâmetro que sua função encapsulada deve ter. Esta é uma declaração de delegado definida pelo usuário:
public delegate void PrimeFoundHandler(int result);
Os delegados são mais comumente usados em conjunto com eventos. Um evento tem um tipo de delegado, da mesma forma que uma classe pode ter um tipo de interface. O delegado representa um contrato que os manipuladores de eventos cumprem muito. Aqui está um membro da classe de evento cujo tipo é o delegado definido anteriormente:
event PrimeFoundHandler^ primeFoundEvent;
Ao declarar delegados que serão expostos a clientes na interface binária do aplicativo do Tempo de Execução do Windows, use Windows::Foundation::TypedEventHandler<TSender, TResult>. Este delegado tem binários de proxy e stub predefinidos que permitem que ele seja consumido por clientes JavaScript.
Consumindo delegados
Ao criar um aplicativo da Plataforma Universal do Windows, você geralmente trabalha com um delegado como o tipo de evento que uma classe do Tempo de Execução do Windows expõe. Para se inscrever em um evento, crie uma instância de seu tipo de delegado especificando uma função — ou lambda — que corresponda à assinatura do delegado. Em seguida, use o += operador para passar o objeto delegado para o membro do evento na classe. Isto é conhecido como subscrever o evento. Quando a instância de classe "dispara" o evento, sua função é chamada, juntamente com quaisquer outros manipuladores que foram adicionados pelo seu objeto ou outros objetos.
Sugestão
Visual Studio faz muito trabalho para você quando você cria um manipulador de eventos. Por exemplo, se você especificar um manipulador de eventos na marcação XAML, uma dica de ferramenta será exibida. Se você escolher a dica de ferramenta, o Visual Studio criará automaticamente o método manipulador de eventos e o associará ao evento na classe de publicação.
O exemplo a seguir mostra o padrão básico.
Windows::Foundation::TypedEventHandler é o tipo de delegado. A função manipulador é criada usando uma função nomeada.
Em 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;
};
Em 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)
{/*...*/}
}
Advertência
Em geral, para um manipulador de eventos, é melhor usar uma função nomeada em vez de um lambda, a menos que você tome muito cuidado para evitar referências circulares. Uma função nomeada captura o ponteiro "this" por referência fraca, mas um lambda o captura por referência forte e cria uma referência circular. Para obter mais informações, consulte Referências fracas e ciclos de quebra.
Por convenção, os nomes de delegados do manipulador de eventos definidos pelo Tempo de Execução do Windows têm o formato *EventHandler — por exemplo, RoutedEventHandler, SizeChangedEventHandler ou SuspendingEventHandler. Também por convenção, os delegados do manipulador de eventos têm dois parâmetros e retornam void. Em um delegado que não tem parâmetros de tipo, o primeiro parâmetro é do tipo Platform::Object^; ele contém uma referência ao remetente, que é o objeto que disparou o evento. Você precisa voltar para o tipo original antes de usar o argumento no método manipulador de eventos. Em um delegado do manipulador de eventos que tem parâmetros type, o primeiro parâmetro type especifica o tipo do remetente e o segundo parâmetro é um identificador para uma classe ref que contém informações sobre o evento. Por convenção, essa classe é chamada *EventArgs. Por exemplo, um delegado RoutedEventHandler tem um segundo parâmetro do tipo RoutedEventArgs^ e DragEventHander tem um segundo parâmetro do tipo DragEventArgs^.
Por convenção, os delegados que encapsulam o código que é executado quando uma operação assíncrona é concluída são nomeados *CompletedHandler. Esses delegados são definidos como propriedades na classe, não como eventos. Portanto, você não usa o += operador para assiná-los, apenas atribui um objeto delegado à propriedade.
Sugestão
C++ IntelliSense não mostra a assinatura de delegado completa; portanto, ele não ajuda a determinar o tipo específico do parâmetro EventArgs. Para encontrar o tipo, você pode ir para o Pesquisador de Objetos e examinar o Invoke método para o delegado.
Criação de delegados personalizados
Você pode definir seus próprios delegados, para definir manipuladores de eventos ou para permitir que os consumidores passem a funcionalidade personalizada para o componente do Tempo de Execução do Windows. Como qualquer outro tipo de Tempo de Execução do Windows, um delegado público não pode ser declarado como genérico.
Declaração
A declaração de um delegado se assemelha a uma declaração de função, exceto que o delegado é um tipo. Normalmente, você declara um delegado no escopo do namespace, embora também possa aninhar uma declaração de delegado em uma declaração de classe. O delegado a seguir encapsula qualquer função que tome um ContactInfo^ como entrada e retorne um Platform::String^arquivo .
public delegate Platform::String^ CustomStringDelegate(ContactInfo^ ci);
Depois de declarar um tipo de delegado, você pode declarar membros de classe desse tipo ou métodos que usam objetos desse tipo como parâmetros. Um método ou função também pode retornar um tipo de delegado. No exemplo a seguir, o ToCustomString método usa o delegado como um parâmetro de entrada. O método permite que o código do cliente forneça uma função personalizada que constrói uma cadeia de caracteres a partir de algumas ou todas as propriedades públicas de um ContactInfo objeto.
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);
}
};
Observação
Você usa o símbolo "^" quando se refere ao tipo de delegado, assim como faz com qualquer tipo de referência do Tempo de Execução do Windows.
Uma declaração de evento sempre tem um tipo de delegado. Este exemplo mostra uma assinatura típica de tipo de delegado no Tempo de Execução do Windows:
public delegate void RoutedEventHandler(
Platform::Object^ sender,
Windows::UI::Xaml::RoutedEventArgs^ e
);
O Click evento na Windows:: UI::Xaml::Controls::Primitives::ButtonBase classe é do tipo RoutedEventHandler. Para obter mais informações, consulte Eventos.
O código do cliente primeiro constrói a instância delegada usando ref new e fornecendo um lambda que é compatível com a assinatura do delegado e define o comportamento personalizado.
CustomStringDelegate^ func = ref new CustomStringDelegate([] (ContactInfo^ c)
{
return c->FirstName + " " + c->LastName;
});
Em seguida, chama a função de membro e passa o delegado. Suponha que ci é uma ContactInfo^ instância e textBlock é um XAML TextBlock^.
textBlock->Text = ci->ToCustomString( func );
No próximo exemplo, um aplicativo cliente passa um delegado personalizado para um método público em um componente do Tempo de Execução do Windows que executa o delegado em relação a cada item em um 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;
}
Construção
Você pode construir um delegado a partir de qualquer um destes objetos:
lambda
função estática
ponteiro para membro
std::função
O exemplo a seguir mostra como construir um delegado a partir de cada um desses objetos. Você consome o delegado exatamente da mesma maneira, independentemente do tipo de objeto usado para construí-lo.
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);
Advertência
Se você usar um lambda que captura o ponteiro "this", certifique-se de usar o -= operador para cancelar explicitamente o registro do evento antes de sair do lambda. Para obter mais informações, consulte Eventos.
Delegados genéricos
Os delegados genéricos em C++/CX têm restrições semelhantes às declarações de classes genéricas. Não podem ser declarados públicos. Você pode declarar um delegado genérico privado ou interno e consumi-lo a partir de C++, mas os clientes .NET ou JavaScript não podem consumi-lo porque ele não é emitido nos metadados .winmd. Este exemplo declara um delegado genérico que só pode ser consumido por C++:
generic <typename T>
delegate void MyEventHandler(T p1, T p2);
O próximo exemplo declara uma instância especializada do delegado dentro de uma definição de classe:
MyEventHandler<float>^ myDelegate;
Delegados e tópicos
Um delegado, assim como um objeto de função, contém código que será executado em algum momento no futuro. Se o código que cria e passa o delegado, e a função que aceita e executa o delegado, estão sendo executados no mesmo thread, então as coisas são relativamente simples. Se esse thread for o thread da interface do usuário, o delegado poderá manipular diretamente objetos da interface do usuário, como controles XAML.
Se um aplicativo cliente carregar um componente do Tempo de Execução do Windows executado em um apartamento encadeado e fornecer um delegado a esse componente, por padrão, o delegado será invocado diretamente no thread STA. A maioria dos componentes do Tempo de Execução do Windows pode ser executada em STA ou MTA.
Se o código que executa o delegado estiver sendo executado em um thread diferente, por exemplo, dentro do contexto de um objeto concurrency::task, você será responsável por sincronizar o acesso aos dados compartilhados. Por exemplo, se seu delegado contiver uma referência a um Vetor e um controle XAML tiver uma referência a esse mesmo Vetor, você deverá tomar medidas para evitar deadlocks ou condições de corrida que possam ocorrer quando o delegado e o controle XAML tentarem acessar o Vetor ao mesmo tempo. Você também deve tomar cuidado para que o delegado não tente capturar por referência variáveis locais que possam sair do escopo antes que o delegado seja invocado.
Se você quiser que o delegado criado seja chamado de volta no mesmo thread em que foi criado — por exemplo, se você passá-lo para um componente executado em um apartamento MTA — e quiser que ele seja invocado no mesmo thread que o criador, use a sobrecarga do construtor delegado que usa um segundo CallbackContext parâmetro. Use essa sobrecarga apenas em delegados que tenham um proxy/stub registrado; nem todos os delegados definidos em Windows.winmd estão registrados.
Se você estiver familiarizado com manipuladores de eventos no .NET, sabe que a prática recomendada é fazer uma cópia local de um evento antes de dispará-lo. Isso evita condições de corrida nas quais um manipulador de eventos pode ser removido pouco antes do evento ser invocado. Não é necessário fazer isso em C++/CX porque quando manipuladores de eventos são adicionados ou removidos, uma nova lista de manipuladores é criada. Como um objeto C++ incrementa a contagem de referência na lista de manipuladores antes de invocar um evento, é garantido que todos os manipuladores serão válidos. No entanto, isso também significa que, se você remover um manipulador de eventos no thread de consumo, esse manipulador ainda poderá ser invocado se o objeto de publicação ainda estiver operando em sua cópia da lista, que agora está desatualizada. O objeto de publicação não obterá a lista atualizada até a próxima vez que acionar o evento.
Ver também
Sistema de tipo
Referência da linguagem C++/CX
Referência de namespaces