Postupy: Definice a používání delegátů (C++/CLI)

Tento článek ukazuje, jak definovat a využívat delegáty v C++/CLI.

I když rozhraní .NET Framework poskytuje řadu delegátů, někdy budete muset definovat nové delegáty.

Následující příklad kódu definuje delegáta, který má název MyCallback. Kód pro zpracování událostí – funkce, která se volá při spuštění tohoto nového delegáta – musí mít návratový typ void a vzít String odkaz.

Hlavní funkce používá statickou metodu definovanou SomeClass k vytvoření instance delegáta MyCallback . Delegát se pak stane alternativní metodou volání této funkce, jak ukazuje odeslání řetězce "single" do objektu delegáta. V dalším kroku jsou další instance vzájemně propojeny MyCallback a následně spouštěny jedním voláním objektu delegáta.

// 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

Následující ukázka kódu ukazuje, jak přidružit delegáta k členu třídy hodnot.

// 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

Vytváření delegátů

Pomocí operátoru "-" můžete odebrat delegáta komponenty z složeného delegáta.

// 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");
}

Výstup

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

Předání delegáta^ nativní funkci, která očekává ukazatel funkce

Ze spravované komponenty můžete volat nativní funkci s parametry ukazatele funkce, kde nativní funkce pak může volat členskou funkci delegáta spravované komponenty.

Tato ukázka vytvoří .dll, která exportuje nativní funkci:

// 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");
   }
}

Další ukázka využívá knihovnu .dll a předá popisovač delegáta nativní funkci, která očekává ukazatel funkce.

// 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
}

Výstup

Call to Managed Function

Přidružení delegátů k nespravovaným funkcím

Chcete-li přidružit delegáta k nativní funkci, musíte zabalit nativní funkci do spravovaného typu a deklarovat funkci, která má být vyvolána prostřednictvím 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;
}

Výstup

hello

Použití nevázaných delegátů

Pomocí nevázaného delegáta můžete předat instanci typu, jejíž funkci chcete volat při volání delegáta.

Nevázaní delegáti jsou zvlášť užitečné, pokud chcete iterovat objekty v kolekci – pomocí každého z nich v klíčových slovech – a volat členské funkce pro každou instanci.

Tady je postup, jak deklarovat, vytvořit instanci a volat vázané a nevázané delegáty:

Akce Vázané delegáty Nevázaní delegáti
Deklarovat Podpis delegáta musí odpovídat podpisu funkce, kterou chcete volat prostřednictvím delegáta. Prvním parametrem podpisu delegáta je typ this objektu, který chcete volat.

Po prvním parametru se podpis delegáta musí shodovat s podpisem funkce, kterou chcete volat prostřednictvím delegáta.
Konkretizovat Při vytváření instance vázaného delegáta můžete zadat funkci instance nebo globální nebo statickou členovou funkci.

Pokud chcete zadat funkci instance, první parametr je instance typu, jehož členovou funkci chcete volat, a druhý parametr je adresa funkce, kterou chcete volat.

Pokud chcete volat globální nebo statickou členovou funkci, stačí předat název globální funkce nebo název statické členské funkce.
Když vytvoříte instanci nevázaného delegáta, stačí předat adresu funkce, kterou chcete volat.
Call Při volání vázaného delegáta stačí předat parametry vyžadované podpisem delegáta. Totéž jako vázaný delegát, ale mějte na paměti, že první parametr musí být instancí objektu, který obsahuje funkci, kterou chcete volat.

Tato ukázka ukazuje, jak deklarovat, vytvořit instanci a volat nevázané delegáty:

// 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();
}

Výstup

2
3
2
3
2
7
8
7
8
7

Další ukázka ukazuje, jak používat nevázané delegáty a pro každý z nich v klíčových slovech iterovat objekty v kolekci a volat členské funkce v každé instanci.

// 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();
}

Tato ukázka vytvoří nevázaný delegát na funkce přístupového objektu vlastnosti:

// 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));
}

Výstup

11

Následující ukázka ukazuje, jak vyvolat delegáta vícesměrového vysílání, kde je jedna instance svázaná a jedna instance je nevázaná.

// 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);
};

Výstup

in f(R ^ r)
in f()

Následující ukázka ukazuje, jak vytvořit a volat nevázaný obecný delegát.

// 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) );
}

Výstup

12
14

Viz také

delegate (rozšíření komponent C++)