Koleksi dengan C++/WinRT

Secara internal, koleksi Windows Runtime memiliki banyak bagian bergerak yang rumit. Tetapi ketika Anda ingin meneruskan objek koleksi ke fungsi Windows Runtime, atau untuk mengimplementasikan properti koleksi dan jenis koleksi Anda sendiri, ada fungsi dan kelas dasar di C++/WinRT untuk mendukung Anda. Fitur-fitur ini mengambil kompleksitas dari tangan Anda, dan menghemat banyak overhead dalam waktu dan upaya.

IVector adalah antarmuka Windows Runtime yang diimplementasikan oleh koleksi elemen akses acak apa pun. Jika Anda mengimplementasikan IVector sendiri, Anda juga perlu menerapkan IIterable, IVectorView, dan IIterator. Bahkan jika Anda membutuhkan jenis koleksi kustom, itu banyak pekerjaan. Tetapi jika Anda memiliki data di std::vector (atau std::map, atau std::unordered_map) dan yang ingin Anda lakukan adalah meneruskannya ke Windows Runtime API, maka Anda ingin menghindari melakukan tingkat pekerjaan itu, jika memungkinkan. Dan menghindari itu mungkin, karena C++/WinRT membantu Anda membuat koleksi secara efisien dan dengan sedikit usaha.

Lihat juga kontrol item XAML; ikat ke koleksi C++/WinRT.

Fungsi pembantu untuk koleksi

Koleksi tujuan umum, kosong

Bagian ini mencakup skenario di mana Anda ingin membuat koleksi yang awalnya kosong; kemudian isi setelah pembuatan.

Untuk mengambil objek baru dari jenis yang mengimplementasikan koleksi tujuan umum, Anda dapat memanggil templat fungsi winrt::single_threaded_vector . Objek dikembalikan sebagai IVector, dan itulah antarmuka tempat Anda memanggil fungsi dan properti objek yang dikembalikan.

Jika Anda ingin menyalin-tempel contoh kode berikut langsung ke file kode sumber utama proyek Aplikasi Konsol Windows (C++/WinRT), maka pertama-tama atur Tidak Menggunakan Header yang Telah Dikompilasi di properti proyek.

// main.cpp
#include <winrt/Windows.Foundation.Collections.h>
#include <iostream>
using namespace winrt;

int main()
{
    winrt::init_apartment();

    Windows::Foundation::Collections::IVector<int> coll{ winrt::single_threaded_vector<int>() };
    coll.Append(1);
    coll.Append(2);
    coll.Append(3);

    for (auto const& el : coll)
    {
        std::cout << el << std::endl;
    }

    Windows::Foundation::Collections::IVectorView<int> view{ coll.GetView() };
}

Seperti yang Anda lihat dalam contoh kode di atas, setelah membuat koleksi, Anda dapat menambahkan elemen, melakukan iterasi di atasnya, dan umumnya memperlakukan objek seperti yang Anda lakukan pada objek koleksi Windows Runtime yang mungkin telah Anda terima dari API. Jika Anda memerlukan tampilan yang tidak dapat diubah atas koleksi, maka Anda dapat memanggil IVector::GetView, seperti yang ditunjukkan. Pola yang ditunjukkan di atas—membuat dan mengkonsumsi koleksi—sesuai untuk skenario sederhana tempat Anda ingin meneruskan data, atau mengeluarkan data dari API. Anda dapat meneruskan IVector, atau IVectorView, di mana saja IIterable diharapkan.

Dalam contoh kode di atas, panggilan ke winrt::init_apartment menginisialisasi utas di Windows Runtime; secara default, di apartemen multithreaded. Panggilan juga menginisialisasi COM.

Pengumpulan tujuan umum, prima dari data

Bagian ini mencakup skenario di mana Anda ingin membuat koleksi dan mengisinya secara bersamaan.

Anda dapat menghindari overhead panggilan ke Tambahkan dalam contoh kode sebelumnya. Anda mungkin sudah memiliki data sumber, atau Anda mungkin lebih suka mengisi data sumber sebelum membuat objek pengumpulan Windows Runtime. Berikut adalah cara untuk melakukan itu.

auto coll1{ winrt::single_threaded_vector<int>({ 1,2,3 }) };

std::vector<int> values{ 1,2,3 };
auto coll2{ winrt::single_threaded_vector<int>(std::move(values)) };

for (auto const& el : coll2)
{
    std::cout << el << std::endl;
}

Anda dapat meneruskan objek sementara yang berisi data Anda ke winrt::single_threaded_vector, seperti coll1halnya , di atas. Atau Anda dapat memindahkan std::vector (dengan asumsi Anda tidak akan mengaksesnya lagi) ke dalam fungsi. Dalam kedua kasus, Anda meneruskan rvalue ke dalam fungsi. Itu memungkinkan pengkompilasi menjadi efisien dan untuk menghindari penyalinan data. Jika Anda ingin mengetahui selengkapnya tentang rvalue, lihat Kategori nilai, dan referensinya.

Jika Anda ingin mengikat kontrol item XAML ke koleksi Anda, maka Anda dapat. Tetapi ketahuilah bahwa untuk mengatur properti ItemsControl.ItemsSource dengan benar, Anda perlu mengaturnya ke nilai jenis IVector IInspectable(atau jenis interoperabilitas seperti IBindableObservableVector).

Berikut adalah contoh kode yang menghasilkan kumpulan jenis yang cocok untuk pengikatan, dan menambahkan elemen ke dalamnya. Anda dapat menemukan konteks untuk contoh kode ini di kontrol item XAML; ikat ke koleksi C++/WinRT.

auto bookSkus{ winrt::single_threaded_vector<Windows::Foundation::IInspectable>() };
bookSkus.Append(winrt::make<Bookstore::implementation::BookSku>(L"Moby Dick"));

Anda dapat membuat koleksi Windows Runtime dari data, dan mendapatkan tampilan siap untuk meneruskan ke API, semua tanpa menyalin apa pun.

std::vector<float> values{ 0.1f, 0.2f, 0.3f };
Windows::Foundation::Collections::IVectorView<float> view{ winrt::single_threaded_vector(std::move(values)).GetView() };

Dalam contoh di atas, koleksi yang kita buat dapat terikat ke kontrol item XAML; tetapi koleksi tidak dapat diamati.

Koleksi yang dapat diamati

Untuk mengambil objek baru dari jenis yang mengimplementasikan koleksi yang dapat diamati, panggil templat fungsi winrt::single_threaded_observable_vector dengan jenis elemen apa pun. Tetapi untuk membuat koleksi yang dapat diamati cocok untuk mengikat kontrol item XAML, gunakan IInspectable sebagai jenis elemen.

Objek dikembalikan sebagai IObservableVector, dan itulah antarmuka tempat Anda (atau kontrol yang terikat) memanggil fungsi dan properti objek yang dikembalikan.

auto bookSkus{ winrt::single_threaded_observable_vector<Windows::Foundation::IInspectable>() };

Untuk detail selengkapnya, dan contoh kode, tentang mengikat kontrol antarmuka pengguna (UI) Anda ke koleksi yang dapat diamati, lihat kontrol item XAML; mengikat koleksi C++/WinRT.

Koleksi asosiatif (peta)

Ada versi koleksi asosiatif dari dua fungsi yang telah kita lihat.

Anda dapat secara opsional melakukan prime koleksi ini dengan data dengan meneruskan ke fungsi rvalue jenis std::map atau std::unordered_map.

auto coll1{
    winrt::single_threaded_map<winrt::hstring, int>(std::map<winrt::hstring, int>{
        { L"AliceBlue", 0xfff0f8ff }, { L"AntiqueWhite", 0xfffaebd7 }
    })
};

std::map<winrt::hstring, int> values{
    { L"AliceBlue", 0xfff0f8ff }, { L"AntiqueWhite", 0xfffaebd7 }
};
auto coll2{ winrt::single_threaded_map<winrt::hstring, int>(std::move(values)) };

Utas tunggal

"Utas tunggal" dalam nama fungsi-fungsi ini menunjukkan bahwa fungsi tersebut tidak memberikan konkurensi apa pun—dengan kata lain, fungsi tersebut tidak aman untuk utas. Penyebutan utas tidak terkait dengan apartemen, karena objek yang dikembalikan dari fungsi-fungsi ini semuanya gesit (lihat Objek tangkas di C++/WinRT). Hanya saja objeknya berulir tunggal. Dan itu sepenuhnya sesuai jika Anda hanya ingin meneruskan data satu arah atau yang lain di seluruh antarmuka biner aplikasi (ABI).

Kelas dasar untuk koleksi

Jika, untuk fleksibilitas lengkap, Anda ingin menerapkan koleksi kustom Anda sendiri, maka Anda harus menghindari melakukan itu dengan cara yang sulit. Misalnya, seperti inilah tampilan vektor kustom tanpa bantuan kelas dasar C++/WinRT.

...
using namespace winrt;
using namespace Windows::Foundation::Collections;
...
struct MyVectorView :
    implements<MyVectorView, IVectorView<float>, IIterable<float>>
{
    // IVectorView
    float GetAt(uint32_t const) { ... };
    uint32_t GetMany(uint32_t, winrt::array_view<float>) const { ... };
    bool IndexOf(float, uint32_t&) { ... };
    uint32_t Size() { ... };

    // IIterable
    IIterator<float> First() const { ... };
};
...
IVectorView<float> view{ winrt::make<MyVectorView>() };

Sebaliknya, jauh lebih mudah untuk mendapatkan tampilan vektor kustom Anda dari templat struct winrt::vector_view_base , dan hanya mengimplementasikan fungsi get_container untuk mengekspos kontainer yang menyimpan data Anda.

struct MyVectorView2 :
    implements<MyVectorView2, IVectorView<float>, IIterable<float>>,
    winrt::vector_view_base<MyVectorView2, float>
{
    auto& get_container() const noexcept
    {
        return m_values;
    }

private:
    std::vector<float> m_values{ 0.1f, 0.2f, 0.3f };
};

Kontainer yang dikembalikan oleh get_container harus menyediakan antarmuka awal dan akhir yang winrt::vector_view_base harapkan. Seperti yang ditunjukkan pada contoh di atas, std::vector menyediakannya. Tetapi Anda dapat mengembalikan kontainer apa pun yang memenuhi kontrak yang sama, termasuk kontainer kustom Anda sendiri.

struct MyVectorView3 :
    implements<MyVectorView3, IVectorView<float>, IIterable<float>>,
    winrt::vector_view_base<MyVectorView3, float>
{
    auto get_container() const noexcept
    {
        struct container
        {
            float const* const first;
            float const* const last;

            auto begin() const noexcept
            {
                return first;
            }

            auto end() const noexcept
            {
                return last;
            }
        };

        return container{ m_values.data(), m_values.data() + m_values.size() };
    }

private:
    std::array<float, 3> m_values{ 0.2f, 0.3f, 0.4f };
};

Ini adalah kelas dasar yang disediakan C++/WinRT untuk membantu Anda menerapkan koleksi kustom.

winrt::vector_view_base

Lihat contoh kode di atas.

winrt::vector_base

struct MyVector :
    implements<MyVector, IVector<float>, IVectorView<float>, IIterable<float>>,
    winrt::vector_base<MyVector, float>
{
    auto& get_container() const noexcept
    {
        return m_values;
    }

    auto& get_container() noexcept
    {
        return m_values;
    }

private:
    std::vector<float> m_values{ 0.1f, 0.2f, 0.3f };
};

winrt::observable_vector_base

struct MyObservableVector :
    implements<MyObservableVector, IObservableVector<float>, IVector<float>, IVectorView<float>, IIterable<float>>,
    winrt::observable_vector_base<MyObservableVector, float>
{
    auto& get_container() const noexcept
    {
        return m_values;
    }

    auto& get_container() noexcept
    {
        return m_values;
    }

private:
    std::vector<float> m_values{ 0.1f, 0.2f, 0.3f };
};

winrt::map_view_base

struct MyMapView :
    implements<MyMapView, IMapView<winrt::hstring, int>, IIterable<IKeyValuePair<winrt::hstring, int>>>,
    winrt::map_view_base<MyMapView, winrt::hstring, int>
{
    auto& get_container() const noexcept
    {
        return m_values;
    }

private:
    std::map<winrt::hstring, int> m_values{
        { L"AliceBlue", 0xfff0f8ff }, { L"AntiqueWhite", 0xfffaebd7 }
    };
};

winrt::map_base

struct MyMap :
    implements<MyMap, IMap<winrt::hstring, int>, IMapView<winrt::hstring, int>, IIterable<IKeyValuePair<winrt::hstring, int>>>,
    winrt::map_base<MyMap, winrt::hstring, int>
{
    auto& get_container() const noexcept
    {
        return m_values;
    }

    auto& get_container() noexcept
    {
        return m_values;
    }

private:
    std::map<winrt::hstring, int> m_values{
        { L"AliceBlue", 0xfff0f8ff }, { L"AntiqueWhite", 0xfffaebd7 }
    };
};

winrt::observable_map_base

struct MyObservableMap :
    implements<MyObservableMap, IObservableMap<winrt::hstring, int>, IMap<winrt::hstring, int>, IMapView<winrt::hstring, int>, IIterable<IKeyValuePair<winrt::hstring, int>>>,
    winrt::observable_map_base<MyObservableMap, winrt::hstring, int>
{
    auto& get_container() const noexcept
    {
        return m_values;
    }

    auto& get_container() noexcept
    {
        return m_values;
    }

private:
    std::map<winrt::hstring, int> m_values{
        { L"AliceBlue", 0xfff0f8ff }, { L"AntiqueWhite", 0xfffaebd7 }
    };
};

API penting