Поделиться через


Коллекции (C++/CX)

В программе C++/CX можно бесплатно использовать контейнеры стандартной библиотеки шаблонов (STL) или любой другой определяемый пользователем тип коллекции. Однако при передаче коллекций между среда выполнения Windows двоичным интерфейсом приложения (ABI), например в элемент управления XAML или клиенту JavaScript, необходимо использовать среда выполнения Windows типы коллекций.

Среда выполнения Windows определяет интерфейсы для коллекций и связанных типов, а C++/CX предоставляет конкретные реализации C++ в файле заголовка collection.h. На этой иллюстрации показаны связи между типами коллекций:

Схема дерева наследования C плюс C плюс C X для типов коллекций.

  • Класс Platform::Collections::Vector похож на класс std::vector.

  • Класс Platform::Collections::Map похож на класс std::map.

  • Класс Platform::Collections::VectorView и класс Platform::Collections::MapView являются версиями Vector только для чтения и Map.

  • Итераторы определяются в пространстве имен Platform::Collections. Эти итераторы удовлетворяют требованиям итераторов STL и позволяют использовать алгоритмы std::find, std::count_ifи другие алгоритмы STL в любом типе интерфейса Windows::Foundation::Collections или конкретном типе Platform::Collections . Например, это означает, что можно выполнить итерацию коллекции в компоненте среда выполнения Windows, созданном в C# и применить к нему алгоритм STL.

    Внимание

    Итераторы прокси-сервера VectorIterator и VectorViewIterator используют прокси-объекты VectoryProxy<T> и ArrowProxy<T> для включения использования с контейнерами STL. Дополнительные сведения см. в теме "Элементы VectorProxy" далее в этой статье.

  • Типы коллекций C++/CX поддерживают ту же безопасность потоков, что и контейнеры STL.

  • Windows::Foundation::Collections::IObservableVector и Windows::Foundation::Collections::IObservableMap определяют события, инициируемые при разнообразных изменениях коллекции. Реализуя эти интерфейсы, объекты Platform::Collections::Map и Platform::Collections::Vector поддерживают привязку данных с коллекциями XAML. Например, если имеется объект Vector , данные которого связаны с Grid, то при добавлении данных в коллекцию их изменения отражаются в пользовательском интерфейсе таблицы.

Использование класса Vector

Когда класс должен передать контейнер последовательности другому компоненту среда выполнения Windows, используйте Windows::Foundation::Collections:: IVector<T> в качестве типа параметра или возвращаемого типа, и Platform::Collections::Vector<T> в качестве конкретной реализации. При попытке использования типа Vector в качестве открытого возвращаемого значения или параметра возникнет ошибка компилятора C3986. Эту ошибку можно исправить, заменив Vector объектом IVector.

Внимание

В случае передачи последовательности внутри разрабатываемой программы используйте Vector или std::vector , поскольку они более эффективны по сравнению с IVector. Используйте IVector только при передаче контейнера с помощью ABI.

Система типов среда выполнения Windows не поддерживает концепцию сорных массивов, поэтому нельзя передать IVector<Platform::Array<T>> в качестве возвращаемого значения или параметра метода. Для передачи массива массивов или последовательности массивов в ABI используйте IVector<IVector<T>^>.

КлассVector<T> предоставляет методы, необходимые для добавления и удаления элементов коллекции и доступа к ним. Его можно неявно преобразовать в класс IVector<T>. Алгоритмы STL также можно применять к экземплярам Vector<T>. В следующем примере демонстрируются некоторые простейшие варианты использования. Функция начала и функция окончания , используемые здесь, — из пространства имен 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 или пару итераторов, чтобы создать в Vector точке, где вы передаете коллекцию через ABI. В следующем примере показано использование конструктора класса Vector для эффективной инициализации из объекта std::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 и создавать из него Platform::Vector только при передаче контейнера через интерфейс ABI.

Типы значений в объекте Vector

Любой элемент, который необходимо сохранить в объекте Platform::Collections::Vector , должен поддерживать сравнение на равенство (неявно или с помощью пользовательского алгоритма сравнения std::equal_to ). Все ссылочные типы и все скалярные типы неявно поддерживают сравнение на равенство. Для нескалярных типов значений, таких как Windows::FoundationDateTime, или для пользовательских сравнений (например, objA->UniqueID == objB->UniqueID) необходимо предоставить специальный объект функции.

Элементы VectorProxy

Platform::Collections::VectorIterator и Platform::Collections::VectorViewIterator позволяют использовать range for циклы и алгоритмы, такие как std::sort с контейнером IVector<T>. Однако невозможно получить доступ к элементам IVector через отмену ссылки на указатель C++; доступ к ним можно получить только с использованием методов GetAt и SetAt . Поэтому эти итераторы используют прокси-классы Platform::Details::VectorProxy<T> и Platform::Details::ArrowProxy<T> предоставляют доступ к отдельным элементам через *операторы ,>и [] в соответствии с требованиями стандартной библиотеки. Строго говоря, при использовании IVector<Person^> vecтипом *begin(vec) является VectorProxy<Person^>. Однако прокси-объект практически всегда прозрачен для кода. Эти прокси-объекты не документируются, поскольку предназначены исключительно для внутреннего пользования итераторами, однако полезно иметь представление о самом механизме работы.

При использовании цикла на основе for диапазона по IVector контейнерам используйте auto&& для правильной привязки переменных итератора к VectorProxy элементам. При использовании auto&возникает предупреждение компилятора C4239 и VectoryProxy упоминается в тексте предупреждения.

На следующем рисунке показан цикл range for с контейнерами IVector<Person^>. Обратите внимание, что выполнение прекращается в точке останова на строке 64. В окне Быстрая проверка показано, что переменная итератора p , по сути, является объектом VectorProxy<Person^> с переменными-членами m_v и m_i . Однако при вызове GetType для этой переменной она возвращает идентичный тип в экземпляр Personp2. Отсюда вывод: несмотря на то что VectorProxy и ArrowProxy могут отображаться в разделе Быстрая проверка, отладчик устраняет некоторые ошибки в компиляторе или других местах, для которых, как правило, не нужно явно создавать код.

Снимок экрана: отладка VectorProxy в диапазоне на основе цикла.

Один из сценариев, в котором необходимо создать код для прокси-объекта, заключается в следующем: необходимо выполнить операцию dynamic_cast с элементами, например при поиске объектов XAML определенного типа в коллекции элементов UIElement . В этом случае необходимо сначала привести элемент к 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...
        }
    }
}

Использование класса Map

В этом примере показано, как вставлять элементы и находить их в объекте 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, создайте объект Platform::Collections::Map из std::map и верните Map в качестве Windows::Foundation::Collections::IMap. При попытке использования типа Map в качестве открытого возвращаемого значения или параметра возникнет ошибка компилятора C3986. Эту ошибку можно исправить, заменив Map объектом IMap. В некоторых случаях (например, если вы не выполняете большого количества операций поиска или вставки и часто передаете коллекцию через интерфейс ABI) более рационально использовать класс Platform::Collections::Map с самого начала, не затрачивая ресурсы на преобразование типа объекта std::map. В любом случае следует избегать операций поиска и вставки в объектах IMap , поскольку из всех трех типов он обладает самой низкой производительностью. Преобразование в IMap следует выполнять только в момент передачи контейнера через интерфейс ABI.

Типы значений в объекте Map

Элементы в Platform::Collections::Map упорядочены. Любой элемент, который необходимо сохранить в объекте Map , должен поддерживать сравнение "меньше" для строгого слабого порядка (неявно или с помощью пользовательского алгоритма сравнения stl::less ). Скалярные типы поддерживают сравнение неявно. Для нескалярных типов значений, таких как Windows::Foundation::DateTime, или для пользовательских вариантов сравнения (например, objA->UniqueID < objB->UniqueID) необходимо определять специальный алгоритм сравнения.

Типы коллекций

Коллекции подразделяются на четыре категории: изменяемые и доступные только для чтения версии последовательных и ассоциативных коллекций. Кроме того, C++/CX улучшает коллекции, предоставляя три класса итератора, упрощающие доступ к коллекциям.

Элементы изменяемой коллекции можно изменять, тогда как к элементам коллекции, доступной только для чтения (также называемой представлением), можно обращаться только для чтения. Доступ к элементам коллекции Platform::Collections::Vector илиPlatform::Collections:VectorView можно получить с помощью итератора или объекта Vector::GetAt и индекса коллекции. К элементам ассоциативной коллекции можно обращаться с помощью карты коллекции ::Lookup и ключа.

Класс Platform::Collections::Map
Изменяемая ассоциативная коллекция. Элементы объекта Map представляют собой пары "ключ-значение". Поддерживается как поиск значения по связанному с ним ключу, так и перебор всех пар "ключ-значение".

В классахMap и MapView используется шаблон <K, V, C = std::less<K>>; таким образом, алгоритм сравнения можно изменять. Кроме того, в классах Vector и VectorView используется шаблон <T, E = std::equal_to<T>> , поэтому поведение метода IndexOf()можно изменять. Это важно в основном для объектов Vector и VectorView , содержащих структуры значения. Например, чтобы создать вектор<Windows::Foundation::D ateTime, необходимо предоставить пользовательский средство сравнения, так как 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, удовлетворяющий требованиям итератора случайного доступа STL const .

Функции begin() и end()

Чтобы упростить использование STL для обработки Vector, VectorViewMapи MapViewпроизвольных Windows::Foundation::Collections объектов, C++/CX поддерживает перегрузки функций begin Function и end Function, не являющихся членами.

В следующей таблице перечислены все доступные итераторы и функции.

Итераторы Функции
Платформа::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>^)
Платформа::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)

События изменения коллекций

КлассыVector и Map поддерживают привязку данных в коллекциях XAML за счет реализации событий, которые возникают при изменении или сбросе объекта коллекции, а также при вставке, удалении или изменении любого элемента коллекции. Можно разрабатывать собственные типы, поддерживающие привязку данных, но нельзя наследовать от типов Map и Vector , так как эти типы запечатаны.

Делегаты Windows::Foundation::Collections::VectorChangedEventHandler и Windows::Foundation::Collections::MapChangedEventHandler определяют сигнатуры для обработчиков событий для событий изменения коллекции. Открытый класс перечисления Windows::Foundation::Collections::CollectionChange и ссылочные классы Platform::Collection::Details::MapChangedEventArgs и Platform::Collections::Details::VectorChangedEventArgs хранят аргументы события, по которым можно определить его причину. *EventArgs Типы определяются в Details пространстве имен, так как вам не нужно создавать или использовать их явным образом при использовании Map илиVector.

См. также

Система типов
Справочник по языку C++/CX
Справочник по пространствам имен