Partilhar via


Como definir e usar delegados (C++/CLI)

Este artigo mostra como definir e consumir delegados em C++/CLI.

Embora o .NET Framework forneça vários delegados, às vezes você pode ter que definir novos delegados.

O exemplo de código a seguir define um delegado chamado MyCallback. O código de manipulação de eventos — a função que é chamada quando este novo delegado é acionado — deve ter um tipo de retorno void e receber uma referência String.

A função principal usa um método estático definido por SomeClass para instanciar o MyCallback delegado. O delegado, então, torna-se um método alternativo de chamar essa função, como demonstrado pelo envio da cadeia de caracteres "single" para o objeto delegado. Em seguida, instâncias adicionais de MyCallback são vinculadas e, em seguida, executadas por uma chamada para o objeto delegado.

// use_delegate.cpp
// compile with: /clr
using namespace System;

ref class SomeClass
{
public:
   static void Func(String^ str)
   {
      Console::WriteLine("static SomeClass::Func - {0}", str);
   }
};

ref class OtherClass
{
public:
   OtherClass( Int32 n )
   {
      num = n;
   }

   void Method(String^ str)
   {
      Console::WriteLine("OtherClass::Method - {0}, num = {1}",
         str, num);
   }

   Int32 num;
};

delegate void MyCallback(String^ str);

int main( )
{
   MyCallback^ callback = gcnew MyCallback(SomeClass::Func);
   callback("single");

   callback += gcnew MyCallback(SomeClass::Func);

   OtherClass^ f = gcnew OtherClass(99);
   callback += gcnew MyCallback(f, &OtherClass::Method);

   f = gcnew OtherClass(100);
   callback += gcnew MyCallback(f, &OtherClass::Method);

   callback("chained");

   return 0;
}
static SomeClass::Func - single
static SomeClass::Func - chained
static SomeClass::Func - chained
OtherClass::Method - chained, num = 99
OtherClass::Method - chained, num = 100

O próximo exemplo de código mostra como associar um delegado a um membro de uma classe de valor.

// mcppv2_del_mem_value_class.cpp
// compile with: /clr
using namespace System;
public delegate void MyDel();

value class A {
public:
   void func1() {
      Console::WriteLine("test");
   }
};

int main() {
   A a;
   A^ ah = a;
   MyDel^ f = gcnew MyDel(a, &A::func1);   // implicit box of a
   f();
   MyDel^ f2 = gcnew MyDel(ah, &A::func1);
   f2();
}
test
test

Como compor delegados

Você pode usar o operador "-" para remover um delegado de componente de um delegado composto.

// mcppv2_compose_delegates.cpp
// compile with: /clr
using namespace System;

delegate void MyDelegate(String ^ s);

ref class MyClass {
public:
   static void Hello(String ^ s) {
      Console::WriteLine("Hello, {0}!", s);
   }

   static void Goodbye(String ^ s) {
      Console::WriteLine("  Goodbye, {0}!", s);
   }
};

int main() {

   MyDelegate ^ a = gcnew MyDelegate(MyClass::Hello);
   MyDelegate ^ b = gcnew MyDelegate(MyClass::Goodbye);
   MyDelegate ^ c = a + b;
   MyDelegate ^ d = c - a;

   Console::WriteLine("Invoking delegate a:");
   a("A");
   Console::WriteLine("Invoking delegate b:");
   b("B");
   Console::WriteLine("Invoking delegate c:");
   c("C");
   Console::WriteLine("Invoking delegate d:");
   d("D");
}

Realização

Invoking delegate a:
Hello, A!
Invoking delegate b:
  Goodbye, B!
Invoking delegate c:
Hello, C!
  Goodbye, C!
Invoking delegate d:
  Goodbye, D!

Passar um delegate para uma função nativa que espera um apontador de função

A partir de um componente gerenciado, você pode chamar uma função nativa com parâmetros de ponteiro de função, onde a função nativa pode chamar a função de membro do delegado do componente gerenciado.

Este exemplo cria o .dll que exporta a função nativa:

// delegate_to_native_function.cpp
// compile with: /LD
#include < windows.h >
extern "C" {
   __declspec(dllexport)
   void nativeFunction(void (CALLBACK *mgdFunc)(const char* str)) {
      mgdFunc("Call to Managed Function");
   }
}

O próximo exemplo consome o .dll e passa um identificador delegado para a função nativa que espera um função apontador.

// delegate_to_native_function_2.cpp
// compile with: /clr
using namespace System;
using namespace System::Runtime::InteropServices;

delegate void Del(String ^s);
public ref class A {
public:
   void delMember(String ^s) {
      Console::WriteLine(s);
   }
};

[DllImportAttribute("delegate_to_native_function", CharSet=CharSet::Ansi)]
extern "C" void nativeFunction(Del ^d);

int main() {
   A ^a = gcnew A;
   Del ^d = gcnew Del(a, &A::delMember);
   nativeFunction(d);   // Call to native function
}

Realização

Call to Managed Function

Para associar delegados a funções não gerenciadas

Para associar um delegado a uma função nativa, você deve encapsular a função nativa em um tipo gerenciado e declarar a função a ser invocada por meio do PInvoke.

// mcppv2_del_to_umnangd_func.cpp
// compile with: /clr
#pragma unmanaged
extern "C" void printf(const char*, ...);
class A {
public:
   static void func(char* s) {
      printf(s);
   }
};

#pragma managed
public delegate void func(char*);

ref class B {
   A* ap;

public:
   B(A* ap):ap(ap) {}
   void func(char* s) {
      ap->func(s);
   }
};

int main() {
   A* a = new A;
   B^ b = gcnew B(a);
   func^ f = gcnew func(b, &B::func);
   f("hello");
   delete a;
}

Realização

hello

Para usar delegados não acoplados

Você pode usar um delegado não acoplado para passar uma instância do tipo cuja função você deseja chamar quando o delegado é chamado.

Os delegados não vinculados são especialmente úteis se pretender iterar através dos objetos numa coleção — usando as palavras-chave para cada, em — e chamar uma função membro em cada instância.

Veja como declarar, instanciar e chamar delegados vinculados e não vinculados:

Ação Delegados vinculados Delegados não vinculados
Declarar A assinatura do delegado deve corresponder à assinatura da função que você deseja chamar através do delegado. O primeiro parâmetro da assinatura de delegado é o tipo de this para o objeto que você deseja chamar.

Após o primeiro parâmetro, a assinatura do delegado deve corresponder à assinatura da função que você deseja chamar através do delegado.
Instanciar Ao instanciar um delegado vinculado, você pode especificar uma função de instância ou uma função de membro global ou estático.

Para especificar uma função de instância, o primeiro parâmetro é uma instância do tipo cuja função de membro você deseja chamar e o segundo parâmetro é o endereço da função que você deseja chamar.

Se você quiser chamar uma função de membro global ou estático, basta passar o nome de uma função global ou o nome da função de membro estático.
Quando você instanciar um delegado não vinculado, basta passar o endereço da função que você deseja chamar.
Chamada Quando você chama um delegado vinculado, basta passar os parâmetros exigidos pela assinatura do delegado. O mesmo que um delegado vinculado, mas recorde-se de que o primeiro parâmetro deve ser uma instância do objeto que contém a função que pretende chamar.

Este exemplo demonstra como declarar, instanciar e chamar delegados não vinculados:

// unbound_delegates.cpp
// compile with: /clr
ref struct A {
   A(){}
   A(int i) : m_i(i) {}
   void Print(int i) { System::Console::WriteLine(m_i + i);}

private:
   int m_i;
};

value struct V {
   void Print() { System::Console::WriteLine(m_i);}
   int m_i;
};

delegate void Delegate1(A^, int i);
delegate void Delegate2(A%, int i);

delegate void Delegate3(interior_ptr<V>);
delegate void Delegate4(V%);

delegate void Delegate5(int i);
delegate void Delegate6();

int main() {
   A^ a1 = gcnew A(1);
   A% a2 = *gcnew A(2);

   Delegate1 ^ Unbound_Delegate1 = gcnew Delegate1(&A::Print);
   // delegate takes a handle
   Unbound_Delegate1(a1, 1);
   Unbound_Delegate1(%a2, 1);

   Delegate2 ^ Unbound_Delegate2 = gcnew Delegate2(&A::Print);
   // delegate takes a tracking reference (must deference the handle)
   Unbound_Delegate2(*a1, 1);
   Unbound_Delegate2(a2, 1);

   // instantiate a bound delegate to an instance member function
   Delegate5 ^ Bound_Del = gcnew Delegate5(a1, &A::Print);
   Bound_Del(1);

   // instantiate value types
   V v1 = {7};
   V v2 = {8};

   Delegate3 ^ Unbound_Delegate3 = gcnew Delegate3(&V::Print);
   Unbound_Delegate3(&v1);
   Unbound_Delegate3(&v2);

   Delegate4 ^ Unbound_Delegate4 = gcnew Delegate4(&V::Print);
   Unbound_Delegate4(v1);
   Unbound_Delegate4(v2);

   Delegate6 ^ Bound_Delegate3 = gcnew Delegate6(v1, &V::Print);
   Bound_Delegate3();
}

Realização

2
3
2
3
2
7
8
7
8
7

O próximo exemplo mostra como usar delegados não vinculados e as palavras-chave para cada, em para iterar objetos em uma coleção e invocar uma função de membro em cada instância.

// unbound_delegates_2.cpp
// compile with: /clr
using namespace System;

ref class RefClass {
   String^ _Str;

public:
   RefClass( String^ str ) : _Str( str ) {}
   void Print() { Console::Write( _Str ); }
};

delegate void PrintDelegate( RefClass^ );

int main() {
   PrintDelegate^ d = gcnew PrintDelegate( &RefClass::Print );

   array< RefClass^ >^ a = gcnew array<RefClass^>( 10 );

   for ( int i = 0; i < a->Length; ++i )
      a[i] = gcnew RefClass( i.ToString() );

   for each ( RefClass^ R in a )
      d( R );

   Console::WriteLine();
}

Este exemplo cria um delegado desvinculado às funções acessoras de uma propriedade:

// unbound_delegates_3.cpp
// compile with: /clr
ref struct B {
   property int P1 {
      int get() { return m_i; }
      void set(int i) { m_i = i; }
   }

private:
   int m_i;
};

delegate void DelBSet(B^, int);
delegate int DelBGet(B^);

int main() {
   B^ b = gcnew B;

   DelBSet^ delBSet = gcnew DelBSet(&B::P1::set);
   delBSet(b, 11);

   DelBGet^ delBGet = gcnew DelBGet(&B::P1::get);
   System::Console::WriteLine(delBGet(b));
}

Realização

11

O exemplo a seguir mostra como invocar um delegado de multicast, onde uma instância é vinculada e uma instância é desvinculada.

// unbound_delegates_4.cpp
// compile with: /clr
ref class R {
public:
   R(int i) : m_i(i) {}

   void f(R ^ r) {
      System::Console::WriteLine("in f(R ^ r)");
   }

   void f() {
      System::Console::WriteLine("in f()");
   }

private:
   int m_i;
};

delegate void Del(R ^);

int main() {
   R ^r1 = gcnew R(11);
   R ^r2 = gcnew R(12);

   Del^ d = gcnew Del(r1, &R::f);
   d += gcnew Del(&R::f);
   d(r2);
}

Realização

in f(R ^ r)
in f()

O próximo exemplo mostra como criar e chamar um delegado genérico não acoplado.

// unbound_delegates_5.cpp
// compile with: /clr
ref struct R {
   R(int i) : m_i(i) {}

   int f(R ^) { return 999; }
   int f() { return m_i + 5; }

   int m_i;
};

value struct V {
   int f(V%) { return 999; }
   int f() { return m_i + 5; }

   int m_i;
};

generic <typename T>
delegate int Del(T t);

generic <typename T>
delegate int DelV(T% t);

int main() {
   R^ hr = gcnew R(7);
   System::Console::WriteLine((gcnew Del<R^>(&R::f))(hr));

   V v;
   v.m_i = 9;
   System::Console::WriteLine((gcnew DelV<V >(&V::f))(v) );
}

Realização

12
14

Ver também

delegar (extensões de componente C++)