集合 (C++/CX)

在 C++/CX 程序中,你可以任意使用标准模板库 (STL) 容器或任何其他用户定义的集合类型。 但是,当你跨 Windows 运行时应用程序二进制接口 (ABI) 边界来回传递集合时,例如传递到 XAML 控件或 JavaScript 客户端时,必须使用 Windows 运行时集合类型。

Windows 运行时定义集合和相关类型的接口,并且,C++/CX 在 collection.h 头文件中提供具体的 C++ 实现。 下图显示了各个集合类型之间的关系:

Diagram of C plus plus C X inheritance tree for collection types.

向量用法

如果你的类需要将序列容器传递到另一个 Windows 运行时组件,请使用 Windows::Foundation::Collections:: IVector<T> 作为参数或返回类型,并使用 Platform::Collections::Vector<T> 作为具体实现。 如果你尝试在公共返回值或参数中使用 Vector 类型,则将引发编译器错误 C3986。 可以通过将 Vector 更改为 IVector来修复该错误。

重要

如果您要在自己的程序中传递序列,请使用 Vectorstd::vector ,因为它们比 IVector更高效。 在通过 ABI 传递容器时,仅使用 IVector

Windows 运行时类型系统不支持交错数组的概念,因此无法将 IVector<Platform::Array<T>> 作为返回值或方法参数传递。 要跨 ABI 传递交错数组或一系列序列,请使用 IVector<IVector<T>^>

Vector<T> 提供添加、移除和访问集合项所需的方法,并且,它可以隐式转换为 IVector<T>。 还可以对 Vector<T>的实例使用 STL 算法。 下面的示例演示一些基本用法。 此处的 begin 函数end 函数 来自 Platform::Collections 命名空间而非 std 命名空间。

#include <collection.h>
#include <algorithm>
using namespace Platform;
using namespace Platform::Collections;
using namespace Windows::Foundation::Collections;


void Class1::Test()
{
    Vector<int>^ vec = ref new Vector<int>();
    vec->Append(1);
    vec->Append(2);
    vec->Append(3);
    vec->Append(4);
    vec->Append(5);


    auto it = 
        std::find(begin(vec), end(vec), 3);

    int j = *it; //j = 3
    int k = *(it + 1); //or it[1]

    // Find a specified value.
    unsigned int n;         
    bool found = vec->IndexOf(4, &n); //n = 3

    // Get the value at the specified index.
    n = vec->GetAt(4); // n = 3

    // Insert an item.
    // vec = 0, 1, 2, 3, 4, 5
    vec->InsertAt(0, 0);

    // Modify an item.
    // vec = 0, 1, 2, 12, 4, 5,
    vec->SetAt(3, 12);

    // Remove an item.
    //vec = 1, 2, 12, 4, 5 
    vec->RemoveAt(0);

    // vec = 1, 2, 12, 4
    vec->RemoveAtEnd();

    // Get a read-only view into the vector.
    IVectorView<int>^ view = vec->GetView();
}

如果你具有使用 std::vector 的现有代码,而你想在 Windows 运行时组件中重用它,只需使用 Vector 构造函数之一即可,它采用一个 std::vector 或一对迭代器在你将集合传递过 ABI 之处构造一个 Vector。 下面的示例演示如何从 Vectorstd::vector移动构造函数用于高效初始化。 移动操作完成后,原始的 vec 变量不再有效。

//#include <collection.h>
//#include <vector>
//#include <utility> //for std::move
//using namespace Platform::Collections;
//using namespace Windows::Foundation::Collections;
//using namespace std;
IVector<int>^ Class1::GetInts()
{
    vector<int> vec;
    for(int i = 0; i < 10; i++)
    {
        vec.push_back(i);
    }    
    // Implicit conversion to IVector
    return ref new Vector<int>(std::move(vec));
}

如果您有一个必须在将来某个时间点通过 ABI 传递的字符串向量,则您必须确定最初是将字符串创建为 std::wstring 类型还是 Platform::String^ 类型。 如果你必须对字符串执行大量处理,请使用 wstring。 否则,请将字符串创建为 Platform::String^ 类型,这可避免稍后转换它们的开销。 你还必须确定是将这些字符串内部置于 std:vector 还是 Platform::Collections::Vector 中。 作为一种常规做法,先使用 std::vector ,然后在通过 ABI 传递容器时从其创建一个 Platform::Vector

Vector 中的值类型

要存储到 Platform::Collections::Vector 中的任何元素必须支持相等性比较(隐式支持或通过使用你提供的自定义 std::equal_to 运算符支持)。 所有引用类型和所有标量类型都隐式支持相等性比较。 对于非标量值类型(如 Windows::Foundation::DateTime)或自定义比较(如 objA->UniqueID == objB->UniqueID),必须提供自定义函数对象。

VectorProxy 元素

Platform::Collections::VectorIteratorPlatform::Collections::VectorViewIterator 使你能够共同使用 range for 循环和算法(如 std::sort)与 IVector<T> 容器。 但是,无法通过 C++ 指针取消引用来访问 IVector 元素,只能通过 GetAtSetAt 方法访问此类元素。 因此,这些迭代器使用代理类 Platform::Details::VectorProxy<T>Platform::Details::ArrowProxy<T>,按照标准库的要求,通过 *、-> 和 [] 运算符提供对各个元素的访问。 严格来说,给定 IVector<Person^> vec*begin(vec) 的类型为 VectorProxy<Person^>。 不过,代理对象对于你的代码几乎始终是透明的。 我们不讨论这些代理对象,因为它们仅由迭代器在内部使用,但了解该机制的工作原理将非常有用。

IVector 容器使用基于范围的 for 循环时,请使用 auto&& 以使迭代器变量正确绑定到 VectorProxy 元素。 如果使用 auto&,将引发编译器警告 C4239,警告文本中将提到 VectoryProxy

下图演示了针对 range forIVector<Person^>循环。 请注意,执行操作在第 64 行的断点处停止。 “快速监视” 窗口显示迭代器变量 p 实际上是具有 VectorProxy<Person^>m_v 成员变量的 m_i 。 不过,当调用 GetType 时,它将返回与 Person 实例 p2相同的类型。 要点在于,虽然 VectorProxyArrowProxy 可能显示在 “快速监视”、调试器的某些编译器错误或其他位置中,但通常不需要对它们进行显式编码。

Screenshot of debugging VectorProxy in a range based for loop.

必须针对代理对象进行编码的一种情况是,你必须对元素执行 dynamic_cast ,例如你在 UIElement 元素集合中寻找特定类型的 XAML 对象。 在此情况下,必须先将元素强制转换为 Platform::Object^,然后执行动态强制转换:

void FindButton(UIElementCollection^ col)
{
    // Use auto&& to avoid warning C4239
    for (auto&& elem : col)
    {
        Button^ temp = dynamic_cast<Button^>(static_cast<Object^>(elem));
        if (nullptr != temp)
        {
            // Use temp...
        }
    }
}

映射用法

此示例演示如何在 Platform::Collections::Map中插入并查找项目,然后将 Map 作为只读 Windows::Foundation::Collections::IMapView 类型返回。

//#include <collection.h>
//using namespace Platform::Collections;
//using namespace Windows::Foundation::Collections;
IMapView<String^, int>^ Class1::MapTest()
{
    Map<String^, int>^ m = ref new Map<String^, int >();
    m->Insert("Mike", 0);
    m->Insert("Dave", 1);
    m->Insert("Doug", 2);
    m->Insert("Nikki", 3);
    m->Insert("Kayley", 4);
    m->Insert("Alex", 5);
    m->Insert("Spencer", 6);

   // PC::Map does not support [] operator
   int i = m->Lookup("Doug");
   
   return m->GetView();
   
}

通常,对于内部映射功能,出于性能考虑,应尽可能首选 std::map 类型。 如果必须通过 ABI 传递容器,请从 std::map 构造一个 Platform::Collections::Map ,然后将 Map 作为 Windows::Foundation::Collections::IMap返回。 如果你尝试在公共返回值或参数中使用 Map 类型,则将引发编译器错误 C3986。 可以通过将 Map 更改为 IMap来修复该错误。 在某些情况下,例如,如果您不需要大量查找或插入,而需要通过 ABI 频繁传递集合,则在一开始使用 Platform::Collections::Map 可能成本较低,同时可避免转换 std::map的开销。 在任何情况下,应避免在 IMap 上执行查找和插入操作,因为这些是三种类型中性能最差的操作。 仅在你通过 ABI 传递容器之处转换为 IMap

Map 中的值类型

Platform::Collections::Map 中的元素已进行排序。 要存储到 Map 中的任何元素都必须支持严格弱排序中的小于比较(隐式支持或使用您提供的自定义 stl::less 比较运算符支持)。 标量类型隐式支持比较。 对于非标量类型(如 Windows::Foundation::DateTime)或自定义比较(如 objA->UniqueID < objB->UniqueID),你必须提供自定义比较器。

集合类型

集合分为四种类别:序列集合和关联集合各自的可修改版本和只读版本。 此外,C++/CX 通过提供简化集合访问的三个迭代器类增强集合。

可以更改可修改集合的元素,但是,只读集合的元素(称作 视图)只能读取。 使用迭代器或集合的 Vector::GetAt 和索引可以访问 Platform::Collections::VectorPlatform::Collections::VectorView 集合的元素。 可以使用集合的 Map::Lookup 和一个键访问关联集合的元素。

Platform::Collections::Map 类
可修改的关联集合。 映射元素为键/值对。 支持查找键以检索其关联值,也支持遍历所有键值对。

MapMapView<K, V, C = std::less<K>>上模板化,因此,你可以自定义比较器。 此外, VectorVectorView<T, E = std::equal_to<T>> 上模板化,以便你可以自定义 IndexOf()的行为。 这通常对于值结构的 VectorVectorView 非常重要。 例如,若要创建 Vector<Windows::Foundation::DateTime>,必须提供自定义比较运算符,因为 DateTime 不会重载 == 运算符。

Platform::Collections::MapView 类
Map的只读版本。

Platform::Collections::Vector 类
可修改的序列集合。 Vector<T> 支持定时随机访问和分摊定时 追加 操作。

Platform::Collections::VectorView 类
Vector的只读版本。

Platform::Collections::InputIterator 类
满足 STL 输入迭代器要求的 STL 迭代器。

Platform::Collections::VectorIterator 类
满足 STL 可变随机访问迭代器要求的 STL 迭代器。

Platform::Collections::VectorViewIterator 类
满足 STL const 随机访问迭代器要求的 STL 迭代器。

begin() 和 end() 函数

为简化使用 STL 以处理 VectorVectorViewMapMapView 和任意 Windows::Foundation::Collections 对象,C++/CX 支持重载 begin 函数end 函数非成员函数。

下表列出可用迭代器和函数。

迭代器 函数
Platform::Collections::VectorIterator<T>

(在内部存储 Windows::Foundation::Collections:: IVector<T> 和 int。)
begin/ end(Windows::Foundation::Collections:: IVector<T>)
Platform::Collections::VectorViewIterator<T>

(在内部存储 IVectorView<T>^ 和 int。)
begin/ end (IVectorView<T>^)
Platform::Collections::InputIterator<T>

(在内部存储 IIterator<T>^ 和 T。)
begin/ end (IIterable<T>)
Platform::Collections::InputIterator<IKeyValuePair<K, V>^>

(在内部存储 IIterator<T>^ 和 T。)
begin/ end (IMap<K,V>
Platform::Collections::InputIterator<IKeyValuePair<K, V>^>

(在内部存储 IIterator<T>^ 和 T。)
begin/ end (Windows::Foundation::Collections::IMapView)

集合更改事件

VectorMap 支持 XAML 集合中的数据绑定,方式是通过实现在更改或重置集合对象,或在插入、移除或更改集合的任何元素时发生的事件。 您可编写自己的支持数据绑定的类型,尽管因这些类型是密封类型而导致无法从 MapVector 继承。

Windows::Foundation::Collections::VectorChangedEventHandlerWindows::Foundation::Collections::MapChangedEventHandler 委托用于指定集合更改事件的事件处理程序的签名。 Windows::Foundation::Collections::CollectionChange 公共枚举类和 Platform::Collection::Details::MapChangedEventArgsPlatform::Collections::Details::VectorChangedEventArgs ref 类用于存储事件参数以确定引发事件的原因。 *EventArgs 类型是在 Details 命名空间中定义的,因为你在使用 MapVector 时无需显式构造或使用它们。

另请参阅

类型系统
C++/CX 语言参考
命名空间参考