Porady: definiowanie obiektów delegowanych (C++/CLI) oraz korzystanie z nich

W tym artykule przedstawiono sposób definiowania i korzystania z delegatów w języku C++/cli.

Chociaż program .NET Framework udostępnia wiele delegatów, czasami może być konieczne zdefiniowanie nowych delegatów.

Poniższy przykład kodu definiuje delegata o nazwie MyCallback. Kod obsługi zdarzeń — funkcja wywoływana podczas uruchamiania tego nowego delegata — musi mieć typ void zwracany i podjąć String odwołanie.

Funkcja main używa metody statycznej zdefiniowanej przez SomeClass program w celu utworzenia wystąpienia delegata MyCallback . Delegat staje się następnie alternatywną metodą wywoływania tej funkcji, jak pokazano, wysyłając ciąg "pojedynczy" do obiektu delegata. Następnie dodatkowe wystąpienia MyCallback obiektu są połączone razem, a następnie wykonywane przez jedno wywołanie obiektu delegata.

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

W następnym przykładzie kodu pokazano, jak skojarzyć delegata z składową klasy wartości.

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

Jak tworzyć delegatów

Możesz użyć operatora "-", aby usunąć delegata składnika z złożonego delegata.

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

Wyjście

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

Przekazywanie delegata^ do funkcji natywnej, która oczekuje wskaźnika funkcji

Z poziomu składnika zarządzanego można wywołać funkcję natywną z parametrami wskaźnika funkcji, gdzie funkcja natywna może wywołać funkcję składową delegata zarządzanego składnika.

Ten przykład tworzy bibliotekę DLL, która eksportuje funkcję natywną:

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

Następny przykład używa biblioteki DLL i przekazuje dojście delegata do funkcji natywnej, która oczekuje wskaźnika funkcji.

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

Wyjście

Call to Managed Function

Aby skojarzyć delegatów z funkcjami niezarządzanymi

Aby skojarzyć delegata z funkcją natywną, należy opakowować funkcję natywną w typie zarządzanym i zadeklarować funkcję do wywołania za pomocą PInvokemetody .

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

Wyjście

hello

Aby użyć niezwiązanych delegatów

Możesz użyć delegata bez ruchu, aby przekazać wystąpienie typu, którego funkcja ma zostać wywołana, gdy delegat jest wywoływany.

Delegaty bez ruchu są szczególnie przydatne, jeśli chcesz iterować przez obiekty w kolekcji — przy użyciu dla każdego z nich w słowach kluczowych — i wywoływać funkcję składową w każdym wystąpieniu.

Oto jak zadeklarować, utworzyć wystąpienie i wywołać pełnomocników powiązanych i niepowiązanych:

Akcja Powiązane delegaty Niezwiązane obiekty delegowane
Zadeklarować Podpis delegata musi być zgodny z podpisem funkcji, którą chcesz wywołać za pośrednictwem delegata. Pierwszy parametr sygnatury delegata jest typem this obiektu, który chcesz wywołać.

Po pierwszym parametrze podpis delegata musi być zgodny z podpisem funkcji, którą chcesz wywołać za pośrednictwem delegata.
Utworzyć wystąpienia Po utworzeniu wystąpienia pełnomocnika powiązanego można określić funkcję wystąpienia lub funkcję globalną lub statyczną składową.

Aby określić funkcję wystąpienia, pierwszym parametrem jest wystąpienie typu, którego funkcja składowa ma zostać wywołana, a drugi parametr jest adresem funkcji, którą chcesz wywołać.

Jeśli chcesz wywołać funkcję globalną lub statyczną składową, wystarczy przekazać nazwę funkcji globalnej lub nazwę statycznej funkcji składowej.
Po utworzeniu wystąpienia delegata bez ruchu wystarczy przekazać adres funkcji, którą chcesz wywołać.
Wywołanie Podczas wywoływania ograniczonego delegata wystarczy przekazać parametry wymagane przez podpis delegata. Taki sam jak powiązany delegat, ale pamiętaj, że pierwszy parametr musi być wystąpieniem obiektu zawierającego funkcję, którą chcesz wywołać.

W tym przykładzie pokazano, jak zadeklarować, utworzyć wystąpienie i wywołać niezawiązanych delegatów:

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

Wyjście

2
3
2
3
2
7
8
7
8
7

W następnym przykładzie pokazano, jak używać niezwiązanych delegatów i dla każdego elementu w słowach kluczowych do iterowania obiektów w kolekcji i wywoływania funkcji składowej w każdym wystąpieniu.

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

W tym przykładzie tworzony jest delegat bez ruchu przychodzącego do funkcji dostępu właściwości:

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

Wyjście

11

W poniższym przykładzie pokazano, jak wywołać delegata multiemisji, w którym jest powiązane jedno wystąpienie, a jedno wystąpienie jest niezwiązane.

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

Wyjście

in f(R ^ r)
in f()

W następnym przykładzie pokazano, jak utworzyć i wywołać niezwiązanego delegata ogólnego.

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

Wynik

12
14

Zobacz też

delegate (C++ Component Extensions)