Colecciones (C++/CX)

En un programa de C++/CX puedes hacer uso libre de contenedores de la Biblioteca de plantillas estándar (STL) o de cualquier otro tipo de colección definida por el usuario. Sin embargo, cuando pases colecciones en uno u otro sentido a través de la Interfaz binaria de aplicación (ABI) de Windows Runtime (por ejemplo, a un control XAML o a un cliente de JavaScript) debes utilizar los tipos de colección de Windows Runtime.

Windows Runtime define las interfaces para colecciones y tipos relacionados, y C++/CX proporciona las implementaciones de C++ concretas en el archivo de encabezado collection.h. En esta ilustración se muestran las relaciones entre los tipos de colección:

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

Uso de Vector

Cuando su clase tenga que pasar un contenedor de secuencias a otro componente de Windows Runtime, use Windows::Foundation::Collections:: IVector<T> como parámetro o tipo de valor devuelto, y Platform::Collections::Vector<T> como la implementación concreta. Si intentas usar un tipo Vector en un valor devuelto o parámetro público, se producirá el error del compilador C3986. Puedes corregir el error cambiando el tipo Vector a IVector.

Importante

Si pasas una secuencia dentro de tu propia programa, utiliza Vector o std::vector porque son más eficaces que IVector. Utiliza IVector solo cuando pases el contenedor a través de la interfaz binaria de aplicación (ABI).

El sistema de tipos de Windows Runtime no admite el concepto de matrices escalonadas y, por consiguiente, no se puede pasar una clase IVector<Platform::Array<T>> como valor devuelto o parámetro del método. Para pasar una matriz escalonada o un grupo de secuencias a través de la ABI, usa IVector<IVector<T>^>.

Vector<T> proporciona los métodos necesarios para agregar, quitar y tener acceso a los elementos de la colección, y se puede convertir implícitamente a IVector<T>. También puedes utilizar algoritmos de STL en instancias de Vector<T>. En el siguiente ejemplo se muestra algún uso básico. La función begin y la función end proceden aquí del espacio de nombres Platform::Collections , no del espacio de nombres 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();
}

Si tienes código existente que utiliza std::vector y deseas reutilizarlo en un componente de Windows Runtime, solo tienes que usar uno de los constructores Vector que toma un objeto std::vector o un par de iteradores para crear un objeto Vector en el punto donde pasas la colección a través de ABI. En el ejemplo siguiente se muestra la forma de utilizar el constructor de movimiento Vector para la inicialización eficaz de un objeto std::vector. Después de la operación de movimiento, la variable vec original deja de ser válida.

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

Si tienes un vector de cadenas que debes pasar a través de ABI en algún momento futuro, debes decidir si creas las cadenas inicialmente como tipos de std::wstring o como tipos de Platform::String^ . Si tienes que hacer una gran cantidad de procesamiento en las cadenas, utiliza wstring. Si no, crea las cadenas como tipos de Platform::String^ y evita el costo de convertirlas más adelante. También debes decidir si estas cadenas se han de colocar en std:vector o Platform::Collections::Vector internamente. Como regla general, usa std::vector y crea después un objeto Platform::Vector desde él cuando pases el contenedor a través de ABI.

Tipos de valor de Vector

Cualquier elemento que se almacena en un objeto Platform::Collections::Vector debe admitir la comparación de igualdad, ya sea de forma implícita o mediante el comparador std::equal_to personalizado que proporciones. Todos los tipos de referencia y todos los tipos escalares admiten implícitamente comparaciones de igualdad. Para los tipos de valor no escalares como Windows::Foundation::DateTimeo para las comparaciones personalizadas (por ejemplo, objA->UniqueID == objB->UniqueID) debes proporcionar un objeto de función personalizado.

Elementos de VectorProxy

Platform::Collections::VectorIterator and Platform::Collections::VectorViewIterator permiten el uso de bucles range for y algoritmos como std::sort con un contenedor IVector<T>. Sin embargo, no se puede acceder a los elementos de IVector mediante la desreferencia de un puntero de C++; solo se puede acceder a ellos con los métodos GetAt y SetAt . Por tanto, estos iteradores utilizan las clases de proxy Platform::Details::VectorProxy<T> y Platform::Details::ArrowProxy<T> para proporcionar acceso a los distintos elementos con los operadores *, -> y [] tal como STL requiere. En realidad, dado un IVector<Person^> vec, el tipo de *begin(vec) es VectorProxy<Person^>. Sin embargo, el objeto proxy casi siempre es transparente en el código. Estos objetos proxy no están documentados porque solo los usan internamente los operadores, pero es útil conocer cómo funciona el mecanismo.

Cuando usas un bucle for basado en alcance en contenedores IVector, utilizas auto&& para permitir a la variable de iterador enlazar correctamente con los elementos de VectorProxy. Si usas auto&, se genera la advertencia del compilador C4239 y se menciona VectoryProxy en el texto de la advertencia.

En la ilustración siguiente se muestra un bucle range for en un IVector<Person^>. Observa que la ejecución se detiene en el punto de interrupción de la línea 64. En la ventana Inspección rápida se muestra que la variable de iterador p es en realidad un VectorProxy<Person^> que tiene las variables miembro m_v y m_i . Sin embargo, cuando se llama a GetType en esta variable, se devuelve el tipo idéntico a la instancia Person de p2. La clave aquí es que aunque VectorProxy y ArrowProxy pueden aparecer en la ventana Inspección rápida, en algunos errores de compilación del depurador o en otros lugares, normalmente no es necesario escribir explícitamente código para ellos.

Screenshot of debugging VectorProxy in a range based for loop.

Un escenario en el que tendrás código alrededor del objeto proxy es cuando tengas que aplicar dynamic_cast a los elementos (por ejemplo, cuando busques objetos XAML de un tipo determinado en una colección de elementos UIElement ). En este caso, primero debes convertir el elemento en Platform::Object^ y después realizar la conversión dinámica:

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

Uso de mapa

En este ejemplo se muestra cómo insertar elementos y buscarlos en un objeto Platform::Collections::Mapy devolver después el objeto Map como un tipo Windows::Foundation::Collections::IMapView de solo lectura.

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

Normalmente, para la funcionalidad interna de mapas, es preferible el tipo std::map por razones de rendimiento. Si tiene que pasar el contenedor a través de la ABI, crea un objeto Platform::Collections::Map desde std::map y devuelve el objeto Map como un Windows::Foundation::Collections::IMap. Si intentas usar un tipo Map en un valor devuelto o parámetro público, se producirá el error del compilador C3986. Puedes corregir el error cambiando el tipo Map a IMap. En algunos casos (por ejemplo, si no estás realizando un gran número de búsquedas o inserciones y estás pasando la colección a través de ABI con frecuencia), puede ser menos oneroso utilizar Platform::Collections::Map desde el principio y evitar el costo de convertir el objeto std::map. En cualquier caso, conviene que evites operaciones de búsqueda e inserción en un objeto IMap ya que estas son las menos rentables en cuando a rendimiento de los tres tipos. Convierte a IMap solo en el momento de pasar el contenedor a través de ABI.

Tipos de valor de Map

Los elementos de un objeto Platform::Collections::Map están ordenados. Cualquier elemento que se almacena en Map debe admitir una comparación de tipo "menor que" con ordenación parcial estricta, ya sea de forma implícita o utilizando el comparador stl::less que proporciones. Los tipos escalares son compatibles con la comparación de forma implícita. Para los tipos de valor no escalares como Windows::Foundation::DateTime, o para las comparaciones personalizadas (por ejemplo, objA->UniqueID < objB->UniqueID) debes proporcionar un comparador personalizado.

Tipos de colección

Las colecciones se dividen en cuatro categorías: versiones modificables y versiones de solo lectura de colecciones de secuencia y asociativas. Además, C++/CX mejora las colecciones proporcionando tres clases de iterador que simplifican el acceso de las colecciones.

Los elementos de una colección modificable pueden cambiar, pero los elementos de una colección de solo lectura, que se denomina vista, solo se pueden leer. Se puede acceder a los elementos de Platform::Collections::Vector o Platform::Collections::VectorView con un iterador o Vector::GetAt de la colección, y un índice. Se puede acceder a los elementos de una colección asociativa con Map::Lookup de una colección y una clave.

Platform::Collections::Map (Clase)
Una colección asociativa modificable. Los elementos de mapa son pares clave-valor. Se admiten tanto la búsqueda de una clave para recuperar su valor asociado, como el recorrido en iteración de todos los pares clave-valor.

Map y MapView tienen plantillas en <K, V, C = std::less<K>>; por consiguiente, puedes personalizar el comparador. Además, Vector y VectorView tienen plantillas en <T, E = std::equal_to<T>> , por lo que puedes personalizar el comportamiento de IndexOf(). Esto es importante principalmente para Vector y VectorView de los structs de valor. Por ejemplo, para crear un vector <Windows::Foundation::DateTime>, debe proporcionar un comparador personalizado porque DateTime no sobrecarga el operador ==.

Platform::Collections::MapView (Clase)
Una versión de solo lectura de Map.

Platform::Collections::Vector (Clase)
Una colección de secuencias modificable. Vector<T> admite operaciones Append de acceso aleatorio de tiempo constante y de tiempo constante amortizado.

Platform::Collections::VectorView (Clase)
Una versión de solo lectura de Vector.

Platform::Collections::InputIterator (Clase)
Un iterador de STL que satisface los requisitos de un iterador de entrada de STL.

Platform::Collections::VectorIterator (Clase)
Un iterador de STL que satisface los requisitos de un iterador de acceso aleatorio mutable de STL.

Platform::Collections::VectorViewIterator (Clase)
Un iterador de STL que satisface los requisitos de un iterador de acceso aleatorio const de STL.

Funciones begin() y end()

Para simplificar el uso del STL para procesar Vector, VectorView, MapMapView, y objetos arbitrariosWindows::Foundation::Collections, C++/CX admite sobrecargas de las funciones begin Function y end Function que no son miembro.

En la tabla siguiente se enumeran los iteradores y las funciones disponibles.

Iteradores Funciones
Platform::Collections::VectorIterator<T>

(Almacena internamente Windows::Foundation::Collections:: IVector<T> e int).
begin/ end(Windows::Foundation::Collections:: IVector<T>)
Platform::Collections::VectorViewIterator<T>

(Almacena internamente IVectorView<T>^ e int).
begin/ end (IVectorView<T>^)
Platform::Collections::InputIterator<T>

(Almacena internamente IIterator<T>^ y T).
begin/ end (IIterable<T>)
Platform::Collections::InputIterator<IKeyValuePair<K, V>^>

(Almacena internamente IIterator<T>^ y T).
begin/ end (IMap<K,V>.
Platform::Collections::InputIterator<IKeyValuePair<K, V>^>

(Almacena internamente IIterator<T>^ y T).
begin/ end (Windows::Foundation::Collections::IMapView)

Eventos de cambios en colecciones

Vector y Map admiten el enlace de datos en colecciones de XAML mediante la implementación de eventos que se producen cuando se cambia o se restablece un objeto de colección, o cuando se inserta, se quita o se cambia cualquier elemento de una colección. Puedes escribir tus propios tipos que admitan enlace de datos, aunque no puedes heredar de Map o Vector porque esos tipos están sellados.

Los delegados Windows::Foundation::Collections::VectorChangedEventHandler y Windows::Foundation::Collections::MapChangedEventHandler especifican las firmas de los controladores de eventos para los eventos de cambio de la colección. La clase de enumeración pública Windows::Foundation::Collections::CollectionChange y las clases ref Platform::Collection::Details::MapChangedEventArgs y Platform::Collections::Details::VectorChangedEventArgs almacenan los argumentos de eventos para determinar la causa del evento. Los tipos *EventArgs se definen en el espacio de nombres Details porque no tienes que crearlos ni usarlos de forma explícita cuando utilices Map o Vector.

Consulte también

Sistema de tipos
Referencia del lenguaje C++/CX
Referencia de espacios de nombres