使用 C++/WinRT 的集合Collections with C++/WinRT

在内部,Windows 运行时集合具有大量复杂的移动部件。Internally, a Windows Runtime collection has a lot of complicated moving parts. 但要将集合对象传递到 Windows 运行时函数,或要实现自己的集合属性和集合类型时,C++/WinRT 中有函数和基类可以提供支持。But when you want to pass a collection object to a Windows Runtime function, or to implement your own collection properties and collection types, there are functions and base classes in C++/WinRT to support you. 这些功能消除复杂性,并节省大量时间和精力上的开销。These features take the complexity out of your hands, and save you a lot of overhead in time and effort.

IVector 是由元素的任意随机访问集合实现的 Windows 运行时接口****。IVector is the Windows Runtime interface implemented by any random-access collection of elements. 如果要自己实现 IVector,还需要实现 IIterableIVectorViewIIterator****************。If you were to implement IVector yourself, you'd also need to implement IIterable, IVectorView, and IIterator. 即使需要自定义的集合类型,也需要做大量工作**。Even if you need a custom collection type, that's a lot of work. 但如果你在 std::vector(或者 std::map 或 std::unordered_map)中有数据,而你想要做的只是将其传递到 Windows 运行时 API,那么如果可能你会希望避免进行该级别的工作************。But if you have data in a std::vector (or a std::map, or a std::unordered_map) and all you want to do is pass that to a Windows Runtime API, then you'd want to avoid doing that level of work, if possible. 要避免是有可能的,因为 C++/WinRT 可以帮助高效地创建集合,且无需花费太多精力**。And avoiding it is possible, because C++/WinRT helps you to create collections efficiently and with little effort.

另参阅 XAML 项目控件;绑定到 C++/WinRT 集合Also see XAML items controls; bind to a C++/WinRT collection.

集合的帮助程序函数Helper functions for collections

通用集合,空General-purpose collection, empty

本节介绍一个场景,在该场景中你希望创建初始为空的集合;然后在创建完毕后将其填充**。This section covers the scenario where you wish to create a collection that's initially empty; and then populate it after creation.

若要检索实现通用集合的类型的新对象,可以调用 winrt::single_threaded_vector 函数模板****。To retrieve a new object of a type that implements a general-purpose collection, you can call the winrt::single_threaded_vector function template. 该对象作为 IVector 返回,并且它是一个接口,通过它可以调用所返回对象的函数和属性****。The object is returned as an IVector, and that's the interface via which you call the returned object's functions and properties.

若要将以下代码示例直接复制并粘贴到 Windows 控制台应用程序 (C++/WinRT) 项目的主源代码文件中,请先在项目属性中设置“不使用预编译的标头” 。If you want to copy-paste the following code examples directly into the main source code file of a Windows Console Application (C++/WinRT) project, then first set Not Using Precompiled Headers in project properties.

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

如上述代码示例中所示,创建集合之后,你可以追加元素、对它们进行迭代,并且通常可以像处理从 API 接收到的任何 Windows 运行时集合对象那样处理对象。As you can see in the code example above, after creating the collection you can append elements, iterate over them, and generally treat the object as you would any Windows Runtime collection object that you might have received from an API. 如果需要集合的不可变视图,则可以调用 IVector::GetView,如下所示****。If you need an immutable view over the collection, then you can call IVector::GetView, as shown. 如上所示的创建和使用集合的模式适用于想要将数据传入 API 或从 API 获取数据的简单方案。The pattern shown above—of creating and consuming a collection—is appropriate for simple scenarios where you want to pass data into, or get data out of, an API. 可以将 IVector 或 IVectorView 传递到预期 IIterable 的任意位置************。You can pass an IVector, or an IVectorView, anywhere an IIterable is expected.

在上述代码示例中,调用 winrt::init_apartment 会初始化 Windows 运行时中(默认在多线程单元中)的线程****。In the code example above, the call to winrt::init_apartment initializes the thread in the Windows Runtime; by default, in a multithreaded apartment. 该调用还会初始化 COM。The call also initializes COM.

通用集合,从数据准备General-purpose collection, primed from data

本节介绍想要创建并填充集合的场景。This section covers the scenario where you wish to create a collection and populate it at the same time.

你可以避免上述代码示例中调用“追加”的开销****。You can avoid the overhead of the calls to Append in the previous code example. 你可能已拥有源数据,或者可能更倾向于在创建 Windows 运行时集合对象之前填充源数据。You may already have the source data, or you may prefer to populate the source data in advance of creating the Windows Runtime collection object. 下面是操作方法。Here's how to do that.

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

可将包含数据的临时对象传递给 winrt::single_threaded_vector,就像上方传递 coll1 那样****。You can pass a temporary object containing your data to winrt::single_threaded_vector, as with coll1, above. 也可以将 std:: vector(假设不会再次访问)移动到该函数中****。Or you can move a std::vector (assuming you won't be accessing it again) into the function. 在这两种情况下,都会将右值传递到函数**。In both cases, you're passing an rvalue into the function. 这确保编译器能够高效运作并避免复制数据。That enables the compiler to be efficient and to avoid copying the data. 如果想要了解有关右值的详细信息,请参阅值类别以及对它们的引用If you want to know more about rvalues, see Value categories, and references to them.

如果想要将 XAML 项目控件绑定到集合,则可以执行以下操作。If you want to bind a XAML items control to your collection, then you can. 但要明白,若要正确设置 ItemsControl.ItemsSource 属性,需要将其设置为 IInspectable 的 IVector 类型的值,或 IBindableObservableVector 等互操作性类型的值****************。But be aware that to correctly set the ItemsControl.ItemsSource property, you need to set it to a value of type IVector of IInspectable (or of an interoperability type such as IBindableObservableVector).

以下是代码示例,它生成适合绑定的类型集合,并向其追加元素。Here's a code example that produces a collection of a type suitable for binding, and appends an element to it. 可在 XAML 项目控件;绑定到 C++/WinRT 集合中查找到此代码的上下文。You can find the context for this code example in XAML items controls; bind to a C++/WinRT collection.

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

可以从数据创建 Windows 运行时集合,并在集合上准备好视图以便传递给 API,完全无需复制任何内容。You can create a Windows Runtime collection from data, and get a view on it ready to pass to an API, all without copying anything.

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

在上述示例中,我们创建的集合可以绑定到 XAML 项目控件;但是该集合是不可观测的**。In the examples above, the collection we create can be bound to a XAML items control; but the collection isn't observable.

可观测集合Observable collection

若要检索实现可观测集合的类型的新对象,调用具有任何元素类型的 winrt::single_threaded_observable_vector 函数模板******。To retrieve a new object of a type that implements an observable collection, call the winrt::single_threaded_observable_vector function template with any element type. 但要使可观测集合适合绑定到 XAML 项目控件,使用 IInspectable 作为元素类型****。But to make an observable collection suitable for binding to a XAML items control, use IInspectable as the element type.

该对象作为 IObservableVector 返回,并且它是一个接口,你(或它绑定到的控件)可以通过它调用所返回对象的函数和属性****。The object is returned as an IObservableVector, and that's the interface via which you (or the control to which it's bound) call the returned object's functions and properties.

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

有关将用户界面 (UI) 控件绑定到可观测集合的详细信息和代码示例,请参阅 XAML 项目控件;绑定到 C++/WinRT 集合For more details, and code examples, about binding your user interface (UI) controls to an observable collection, see XAML items controls; bind to a C++/WinRT collection.

关联集合(映射)Associative collection (map)

以下是我们介绍的这两个函数的关联集合版本。There are associative collection versions of the two functions that we've looked at.

通过向函数传递 std::map 或 std::unordered_map 类型的右值,可以选择为这些集合准备好数据**********。You can optionally prime these collections with data by passing to the function an rvalue of type std::map or 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)) };

单线程Single-threaded

这些函数名称中的“单线程”表示它们不支持任何并发性—换言之,它们不是线程安全的。The "single-threaded" in the names of these functions indicates that they don't provide any concurrency—in other words, they're not thread-safe. 此处提到的线程与单元无关,因为这些函数所返回的对象都是敏捷的(请参阅 C++/WinRT 中的敏捷对象)。The mention of threads is unrelated to apartments, because the objects returned from these functions are all agile (see Agile objects in C++/WinRT). 但是指对象是单线程的。It's just that the objects are single-threaded. 如果只想跨应用程序二进制接口 (ABI) 以某种方式传递数据,那么这完全合适。And that's entirely appropriate if you just want to pass data one way or the other across the application binary interface (ABI).

集合的基类Base classes for collections

如果想要实现自定义集合以实现完全灵活,最好避免采用这种复杂的方式。If, for complete flexibility, you want to implement your own custom collection, then you'll want to avoid doing that the hard way. 例如,如果没有 C++/WinRT 基类,以下就是自定义矢量视图的外观**。For example, this is what a custom vector view would look like without the assistance of C++/WinRT's base classes.

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

winrt::vector_view_base 结构模板派生自定义矢量视图,并实现 get_container 函数来公开保存数据的容器,这样做要简单得多********。Instead, it's much easier to derive your custom vector view from the winrt::vector_view_base struct template, and just implement the get_container function to expose the container holding your data.

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

get_container 返回的容器必须提供 winrt::vector_view_base 需要的 begin 和 end 接口****************。The container returned by get_container must provide the begin and end interface that winrt::vector_view_base expects. 如上述示例所示,std::vector 支持这一点****。As shown in the example above, std::vector provides that. 但你可以返回任何满足相同协定的容器,包括自定义容器。But you can return any container that fulfils the same contract, including your own custom container.

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

以下是 C++/WinRT 提供的基类,用于帮助实现自定义集合。These are the base classes that C++/WinRT provides to help you implement custom collections.

winrt::vector_view_basewinrt::vector_view_base

请参阅上述代码示例。See the code examples above.

winrt::vector_basewinrt::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_basewinrt::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_basewinrt::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_basewinrt::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_basewinrt::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 }
    };
};

重要的 APIImportant APIs