Delegasi (C++/CX)

Kata delegate kunci digunakan untuk mendeklarasikan jenis referensi yang setara dengan Windows Runtime objek fungsi di C++standar. Deklarasi delegasi yang mirip dengan tanda tangan fungsi; ini menentukan jenis pengembalian dan jenis parameter yang harus dimiliki fungsi yang dibungkus. Ini adalah deklarasi delegasi yang ditentukan pengguna:

public delegate void PrimeFoundHandler(int result);

Delegasi paling umum digunakan bersama dengan peristiwa. Peristiwa memiliki jenis delegasi, dengan cara yang sama seperti kelas dapat memiliki jenis antarmuka. Delegasi mewakili kontrak yang banyak dipenuhi oleh penanganan aktivitas. Berikut adalah anggota kelas peristiwa yang jenisnya adalah delegasi yang ditentukan sebelumnya:

event PrimeFoundHandler^ primeFoundEvent;

Saat mendeklarasikan delegasi yang akan diekspos ke klien di seluruh antarmuka biner aplikasi Windows Runtime, gunakan Windows::Foundation::TypedEventHandler<TSender, TResult>. Delegasi ini memiliki biner proksi dan stub yang telah ditentukan sebelumnya yang memungkinkannya dikonsumsi oleh klien Javascript.

Mengkonsumsi delegasi

Saat membuat aplikasi Platform Windows Universal, Anda sering bekerja dengan delegasi sebagai jenis peristiwa yang diekspos oleh kelas Windows Runtime. Untuk berlangganan peristiwa, buat instans jenis delegasinya dengan menentukan fungsi—atau lambda—yang cocok dengan tanda tangan delegasi. Kemudian gunakan += operator untuk meneruskan objek delegasi ke anggota peristiwa di kelas. Ini dikenal sebagai berlangganan acara. Saat instans kelas "menembakkan" peristiwa, fungsi Anda dipanggil, bersama dengan handler lain yang ditambahkan oleh objek Anda atau objek lainnya.

Tip

Visual Studio melakukan banyak pekerjaan untuk Anda saat Anda membuat penanganan aktivitas. Misalnya, jika Anda menentukan penanganan aktivitas di markup XAML, tip alat akan muncul. Jika Anda memilih tips alat, Visual Studio secara otomatis membuat metode penanganan aktivitas dan mengaitkannya dengan peristiwa di kelas penerbitan.

Contoh berikut menunjukkan pola dasar. Windows::Foundation::TypedEventHandler adalah jenis delegasi. Fungsi handler dibuat dengan menggunakan fungsi bernama.

Di 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;

};

Di 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)
    {/*...*/}

}

Peringatan

Secara umum, untuk penanganan aktivitas, lebih baik menggunakan fungsi bernama alih-alih lambda kecuali Anda berhati-hati untuk menghindari referensi melingkar. Fungsi bernama menangkap penunjuk "ini" dengan referensi lemah, tetapi lambda menangkapnya dengan referensi yang kuat dan membuat referensi melingkar. Untuk informasi selengkapnya, lihat Referensi lemah dan siklus yang melanggar.

Menurut konvensi, nama delegasi penanganan aktivitas yang ditentukan oleh Windows Runtime memiliki formulir *EventHandler—misalnya, RoutedEventHandler, SizeChangedEventHandler, atau SuspendingEventHandler. Juga berdasarkan konvensi, delegasi penanganan aktivitas memiliki dua parameter dan mengembalikan kekosongan. Dalam delegasi yang tidak memiliki parameter jenis, parameter pertama adalah jenis Platform::Object^; ia menyimpan referensi ke pengirim, yang merupakan objek yang menembakkan peristiwa. Anda harus melemparkan kembali ke jenis asli sebelum menggunakan argumen dalam metode penanganan aktivitas. Dalam delegasi penanganan aktivitas yang memiliki parameter jenis, parameter jenis pertama menentukan jenis pengirim, dan parameter kedua adalah handel ke kelas ref yang menyimpan informasi tentang peristiwa tersebut. Menurut konvensi, kelas tersebut diberi nama *EventArgs. Misalnya, delegasi RoutedEventHandler memiliki parameter kedua jenis RoutedEventArgs^, dan DragEventHander memiliki parameter kedua jenis DragEventArgs^.

Menurut konvensi, delegasi yang membungkus kode yang dijalankan ketika operasi asinkron selesai diberi nama *CompletedHandler. Delegasi ini didefinisikan sebagai properti pada kelas, bukan sebagai peristiwa. Oleh karena itu, Anda tidak menggunakan += operator untuk berlangganan; Anda hanya menetapkan objek delegasi ke properti .

Tip

C++ IntelliSense tidak menampilkan tanda tangan delegasi lengkap; oleh karena itu, ini tidak membantu Anda menentukan jenis parameter EventArgs tertentu. Untuk menemukan jenisnya, Anda dapat membuka Browser Objek dan melihat Invoke metode untuk delegasi.

Membuat delegasi kustom

Anda dapat menentukan delegasi Anda sendiri, untuk menentukan penanganan aktivitas atau untuk memungkinkan konsumen meneruskan fungsionalitas kustom ke komponen Windows Runtime Anda. Seperti jenis Windows Runtime lainnya, delegasi publik tidak dapat dideklarasikan sebagai generik.

Deklarasi

Deklarasi delegasi menyerupan deklarasi fungsi kecuali bahwa delegasi adalah jenis. Biasanya, Anda mendeklarasikan delegasi di cakupan namespace layanan, meskipun Anda juga dapat menumpuk deklarasi delegasi dalam deklarasi kelas. Delegasi berikut merangkum fungsi apa pun yang mengambil ContactInfo^ sebagai input dan mengembalikan Platform::String^.

public delegate Platform::String^ CustomStringDelegate(ContactInfo^ ci);

Setelah mendeklarasikan jenis delegasi, Anda dapat mendeklarasikan anggota kelas dari jenis atau metode tersebut yang mengambil objek dari jenis tersebut sebagai parameter. Metode atau fungsi juga dapat mengembalikan jenis delegasi. Dalam contoh berikut, ToCustomString metode mengambil delegasi sebagai parameter input. Metode ini memungkinkan kode klien untuk menyediakan fungsi kustom yang membangun string dari beberapa atau semua properti ContactInfo publik objek.

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

Catatan

Anda menggunakan simbol "^" saat merujuk ke jenis delegasi, seperti yang Anda lakukan dengan jenis referensi Windows Runtime apa pun.

Deklarasi peristiwa selalu memiliki jenis delegasi. Contoh ini memperlihatkan tanda tangan jenis delegasi umum di Windows Runtime:

public delegate void RoutedEventHandler(
    Platform::Object^ sender, 
    Windows::UI::Xaml::RoutedEventArgs^ e
    );

Kejadian Click di Windows:: UI::Xaml::Controls::Primitives::ButtonBase kelas berjenis RoutedEventHandler. Untuk informasi selengkapnya, lihat Peristiwa.

Kode klien terlebih dahulu membuat instans delegasi dengan menggunakan ref new dan menyediakan lambda yang kompatibel dengan tanda tangan delegasi dan menentukan perilaku kustom.

CustomStringDelegate^ func = ref new CustomStringDelegate([] (ContactInfo^ c)
{
    return c->FirstName + " " + c->LastName;
});

Kemudian memanggil fungsi anggota dan meneruskan delegasi. Asumsikan itu ci adalah ContactInfo^ instans dan textBlock merupakan XAML TextBlock^.

textBlock->Text = ci->ToCustomString( func );

Dalam contoh berikutnya, aplikasi klien meneruskan delegasi kustom ke metode publik dalam komponen Windows Runtime yang menjalankan delegasi terhadap setiap item dalam 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;
}

Konstruksi

Anda dapat membuat delegasi dari salah satu objek ini:

  • Lambda

  • fungsi statis

  • pointer-to-member

  • std::function

Contoh berikut menunjukkan cara membuat delegasi dari masing-masing objek ini. Anda menggunakan delegasi dengan cara yang sama persis terlepas dari jenis objek yang digunakan untuk membangunnya.


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

Peringatan

Jika Anda menggunakan lambda yang mengambil pointer "ini", pastikan untuk menggunakan -= operator untuk secara eksplisit membatalkan pendaftaran dari peristiwa sebelum Anda keluar dari lambda. Untuk informasi selengkapnya, lihat Peristiwa.

Delegasi generik

Delegasi generik di C++/CX memiliki batasan yang mirip dengan deklarasi kelas generik. Mereka tidak dapat dinyatakan sebagai publik. Anda dapat mendeklarasikan delegasi generik privat atau internal dan menggunakannya dari C++, tetapi klien .NET atau JavaScript tidak dapat menggunakannya karena tidak dipancarkan ke dalam metadata .winmd. Contoh ini mendeklarasikan delegasi generik yang hanya dapat dikonsumsi oleh C++:

generic <typename T>
delegate void  MyEventHandler(T p1, T p2);

Contoh berikutnya mendeklarasikan instans khusus delegasi di dalam definisi kelas:

MyEventHandler<float>^ myDelegate;

Delegasi dan utas

Delegasi, sama seperti objek fungsi, berisi kode yang akan dijalankan pada beberapa waktu di masa mendatang. Jika kode yang membuat dan meneruskan delegasi, dan fungsi yang menerima dan menjalankan delegasi, berjalan pada utas yang sama, maka semuanya relatif sederhana. Jika utas tersebut adalah utas UI, maka delegasi dapat langsung memanipulasi objek antarmuka pengguna seperti kontrol XAML.

Jika aplikasi klien memuat komponen Windows Runtime yang berjalan di apartemen berutas, dan menyediakan delegasi ke komponen tersebut, maka secara default delegasi dipanggil langsung di utas STA. Sebagian besar komponen Windows Runtime dapat berjalan di STA atau MTA.

Jika kode yang menjalankan delegasi berjalan pada utas yang berbeda—misalnya, dalam konteks konkurensi::objek tugas—maka Anda bertanggung jawab untuk menyinkronkan akses ke data bersama. Misalnya, jika delegasi Anda berisi referensi ke Vektor, dan kontrol XAML memiliki referensi ke Vektor yang sama, Anda harus mengambil langkah-langkah untuk menghindari kebuntuan atau kondisi balapan yang mungkin terjadi ketika delegasi dan kontrol XAML mencoba mengakses Vektor secara bersamaan. Anda juga harus berhati-hati bahwa delegasi tidak mencoba mengambil dengan mereferensikan variabel lokal yang mungkin keluar dari cakupan sebelum delegasi dipanggil.

Jika Anda ingin delegasi yang Anda buat dipanggil kembali pada utas yang sama dengan yang dibuat—misalnya, jika Anda meneruskannya ke komponen yang berjalan di apartemen MTA—dan Anda ingin dipanggil pada utas yang sama dengan pembuat, maka gunakan konstruktor delegasi kelebihan beban yang mengambil parameter kedua CallbackContext . Hanya gunakan kelebihan beban ini pada delegasi yang memiliki proksi/stub terdaftar; tidak semua delegasi yang didefinisikan dalam Windows.winmd terdaftar.

Jika Anda terbiasa dengan penanganan aktivitas di .NET, Anda tahu bahwa praktik yang direkomendasikan adalah membuat salinan lokal peristiwa sebelum Anda mengaktifkannya. Ini menghindari kondisi balapan di mana penanganan aktivitas dapat dihapus tepat sebelum acara dipanggil. Tidak perlu melakukan ini di C++/CX karena ketika penanganan aktivitas ditambahkan atau dihapus daftar handler baru dibuat. Karena objek C++ menaikkan jumlah referensi pada daftar handler sebelum memanggil peristiwa, dijamin bahwa semua handler akan valid. Namun, ini juga berarti bahwa jika Anda menghapus penanganan aktivitas pada utas yang mengonsumsi, handler tersebut mungkin masih dipanggil jika objek penerbitan masih beroperasi pada salinan daftarnya, yang sekarang sudah kedaluarsa. Objek penerbitan tidak akan mendapatkan daftar yang diperbarui sampai kali berikutnya peristiwa diaktifkan.

Baca juga

Sistem Jenis
Referensi Bahasa C++/CX
Referensi Namespace