Collections (C++/CX)

Dans un programme C++/CX, vous pouvez utiliser librement les conteneurs de la bibliothèque standard de modèles (STL) ou tout autre type de collection défini par l’utilisateur. Cependant, lorsque vous passez des collections d’un côté à l’autre de l’interface binaire des applications Windows Runtime (ABI) — par exemple, à un contrôle XAML ou à un client JavaScript — vous devez utiliser des types de collections Windows Runtime.

Windows Runtime définit les interfaces pour les collections et les types associés, et C++/CX fournit les implémentations C++ concrètes dans le fichier d’en-tête collection.h. Cette illustration montre les relations entre les types de collection :

Diagramme de l’arborescence d’héritage C plus plus C X pour les types de collection.

Utilisation de Vector

Lorsque votre classe doit passer un conteneur de séquences à un autre composant Windows Runtime, utilisez Windows::Foundation::Collections::IVector<T> comme paramètre ou type de retour, et Platform::Collections::Vector<T> comme implémentation concrète. Si vous tentez d'utiliser un type Vector dans une valeur ou un paramètre de retour, l'erreur de compilateur C3986 est générée. Vous pouvez corriger l'erreur en remplaçant le Vector par un IVector.

Important

Si vous passez une séquence dans le cadre de votre propre programme, utilisez Vector ou std::vector car ils sont plus efficaces que IVector. Utilisez IVector uniquement lorsque vous passez le conteneur à travers l'ABI.

Le système de types Windows Runtime ne prend pas en charge le concept de tableaux irréguliers et, par conséquent, vous ne pouvez pas passer un IVector<Platform::Array<T>> comme valeur de retour ou paramètre de méthode. Pour passer un tableau en escalier ou une séquence de séquences à travers l'ABI, utilisez IVector<IVector<T>^>.

Vector<T> fournit les méthodes requises pour ajouter, supprimer les éléments et y accéder dans la collection, et il est implicitement convertible en IVector<T>. Vous pouvez également utiliser les algorithmes STL sur les instances de Vector<T>. L'exemple ci-dessous illustre l'utilisation de base. Les fonctions begin et end sont issues ici de l'espace Platform::Collections au lieu de l'espace 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 vous avez du code existant qui utilise std::vector et que vous souhaitez le réutiliser dans un composant Windows Runtime, utilisez simplement l’un des constructeurs Vector qui prend un std::vector ou une paire d’itérateurs pour construire un Vector au point où vous passez la collection à travers l’ABI. L'exemple ci-dessous indique comment utiliser le constructeur de déplacement Vector pour une initialisation efficace à partir d'un std::vector. Une fois l'opération de déplacement terminée, la variable vec d'origine n'est plus valide.

//#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 vous avez un vecteur de chaînes que vous devez passer à travers l'ABI à un futur point, vous devez décider s'il faut créer des chaînes initialement en tant que types std::wstring ou Platform::String^ . Si vous devez effectuer un important traitement sur les chaînes, utilisez wstring. Sinon, créez les chaînes en tant que types Platform::String^ et évitez ainsi d'avoir à les convertir ultérieurement. Vous devez également décider s'il faut placer ces chaînes dans std:vector ou Platform::Collections::Vector en interne. De manière générale, utilisez std::vector et créez ensuite Platform::Vector à partir de ce dernier uniquement lorsque vous passez le conteneur à travers l'ABI.

Types de valeur relatifs au vecteur

Tout élément à stocker dans Platform::Collections::Vector doit prendre en charge la comparaison d'égalité, implicitement ou à l'aide d'un comparateur std::equal_to personnalisé que vous fournissez. Tous les types de références et tous les types scalaires prennent en charge implicitement les comparaisons d'égalité. Pour les types de valeurs non scalaires tels que Windows::Foundation::DateTime, ou pour les comparaisons personnalisées, par exemple, objA->UniqueID == objB->UniqueID, vous devez fournir un objet de fonction personnalisé.

Éléments VectorProxy

Platform::Collections::VectorIterator et Platform::Collections::VectorViewIterator permettent l’utilisation de boucles range for et d’algorithmes comme std::sort avec un conteneur IVector<T>. Toutefois, les éléments IVector ne sont pas accessibles par l’intermédiaire du déréférencement de pointeur C++. Ils sont accessibles uniquement avec les méthodes GetAt et SetAt . Par conséquent, ces itérateurs utilisent les classes proxy Platform::Details::VectorProxy<T> et Platform::Details::ArrowProxy<T> pour fournir un accès aux éléments individuels via les opérateurs *, -> et [], comme l’exige la bibliothèque standard. À proprement parler, avec un IVector<Person^> vec, le type de *begin(vec) est VectorProxy<Person^>. Toutefois, l'objet proxy est presque toujours transparent pour votre code. Ces objets proxy ne sont pas documentés car ils servent uniquement à un usage interne par les itérateurs, mais il est utile de savoir comment le mécanisme fonctionne.

Lorsque vous utilisez une boucle basée sur une plage for sur des conteneurs IVector, utilisez auto&& pour permettre à la variable itérateur de se lier correctement aux éléments VectorProxy. Si vous utilisez auto&, l’avertissement du compilateur C4239 est levé et VectoryProxy est mentionné dans le texte de l’avertissement.

L'illustration suivante montre une boucle range for sur un IVector<Person^>. Notez que l'exécution est interrompue sur le point d'arrêt à la ligne 64. La fenêtre Espion express indique que la variable d'itérateur p est en fait un VectorProxy<Person^> qui a les variables membres m_v et m_i . Toutefois, lorsque vous appelez la méthode GetType sur cette variable, elle retourne le type identique à l'instance Personp2. Le principal avantage est que, bien que VectorProxy et ArrowProxy puissent apparaître dans Espion express, le débogueur de certaines erreurs de compilateur, il n'est pas nécessaire en général de coder explicitement pour eux.

Capture d’écran du débogage de VectorProxy dans une boucle for basée sur une plage.

Un scénario dans lequel vous devez coder autour de l'objet proxy est lorsque vous devez effectuer un dynamic_cast sur les éléments, par exemple lorsque vous recherchez des objets XAML d'un type particulier dans une collection d'éléments UIElement . Dans ce cas, vous devez commencer par caster l'élément en Platform::Objectpuis effectuer le cast dynamique :

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

Utilisation de Map

Cet exemple montre comment insérer des éléments et les consulter dans Platform::Collections::Map, puis retourner Map sous forme d’un type Windows::Foundation::Collections::IMapView en lecture seule.

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

En général, pour les fonctionnalités de carte intégrées, préférez le type std::map pour des raisons de performance. Si vous devez passer le conteneur à travers l’ABI, construisez un Platform::Collections::Map à partir de std::map et retournez le Map sous forme de Windows::Foundation::Collections::IMap. Si vous tentez d'utiliser un type Map dans une valeur ou un paramètre de retour, l'erreur de compilateur C3986 est générée. Vous pouvez corriger l'erreur en remplaçant le Map par un IMap. Dans certains cas, par exemple si vous n'effectuez pas un grand nombre de recherches ou d'insertions et que vous passez fréquemment la collection à travers l'ABI, il peut être moins coûteux d'utiliser Platform::Collections::Map et d'éviter ainsi le coût de conversion de std::map. Dans tous les cas, évitez les recherches et insertions sur un IMap , car c'est le moins performant des trois types. Effectuez une conversion en IMap uniquement au point où vous passez le conteneur à travers l'ABI.

Types de valeur relatifs au mappage

Les éléments contenus dans Platform::Collections::Map sont classés. Tout élément à stocker dans Map doit prendre en charge la comparaison « inférieur à » avec le classement faible strict, implicitement ou à l'aide d'un comparateur personnalisé stl::less que vous fournissez. Les types scalaires prennent en charge la comparaison implicitement. Pour les types de valeurs non scalaires tels que Windows::Foundation::DateTime, ou pour les comparaisons personnalisées, par exemple, objA->UniqueID < objB->UniqueID, vous devez fournir un comparateur personnalisé.

Types de collections

Les collections sont classées en quatre catégories : versions modifiables et versions en lecture seule des collections séquentielles et associatives. De plus, C++/CX améliore les collections en fournissant trois classes d’itérateurs qui simplifient l’accès aux collections.

Les éléments d'une collection modifiable peuvent être modifiés, mais les éléments d'une collection en lecture seule, appelée vue, peuvent uniquement être lus. Les éléments d’une collection Platform::Collections::Vector ou Platform::Collections::VectorView peuvent être accessibles à l’aide d’un itérateur ou via la méthode Vector::GetAt de la collection et un index. Les éléments d’une collection associative peuvent être accessibles via la méthode Map::Lookup de la collection et une clé.

Platform::Collections::Map, classe
Collection associative modifiable. Les éléments de mappage sont des paires clé/valeur. La recherche d'une clé pour récupérer sa valeur associée et l'itération sur toutes les paires clé/valeur sont toutes deux prises en charge.

Map et MapView sont basés sur un modèle de <K, V, C = std::less<K>>; vous pouvez donc personnaliser le comparateur. En outre, Vector et VectorView sont basés sur des modèles de <T, E = std::equal_to<T>> pour vous permettre de personnaliser le comportement de IndexOf(). C'est important principalement pour le Vector et le VectorView des structures de valeur. Par exemple, pour créer un Vector<Windows::Foundation::DateTime>, vous devez fournir un comparateur personnalisé, car DateTime ne surcharge pas l’opérateur ==.

Platform::Collections::MapView, classe
Version en lecture seule d'un Map.

Platform::Collections::Vector, classe
Collection de séquence modifiable. Vector<T> prend en charge les opérations d'accès aléatoire en temps constant et les opérations d' ajout en temps constant amorti.

Platform::Collections::VectorView, classe
Version en lecture seule d'un Vector.

Platform::Collections::InputIterator, classe
Itérateur STL qui satisfait aux spécifications d'un itérateur d'entrée STL.

Platform::Collections::VectorIterator, classe
Itérateur STL qui répond aux spécifications d'un itérateur à accès aléatoire mutable STL.

Platform::Collections::VectorViewIterator, classe
Un itérateur STL qui satisfait aux exigences d’un itérateur const à accès aléatoire STL.

Fonctions begin() et end()

Pour simplifier l’utilisation de la STL pour traiter Vector, VectorView, Map, MapView et des objets Windows::Foundation::Collections arbitraires, C++/CX prend en charge les surcharges des fonctions non-membres begin Function et end Function.

Le tableau ci-dessous répertorie les itérateurs et fonctions disponibles.

Itérateurs Functions
Platform::Collections::VectorIterator<T>

(Stocke en interne Windows::Foundation::Collections::IVector<T> et un int.)
begin/ end(Windows::Foundation::Collections::IVector<T>)
Platform::Collections::VectorViewIterator<T>

(Stocke en interne IVectorView<T>^ et un int.)
begin/ end (IVectorView<T>^)
Platform::Collections::InputIterator<T>

(Stocke en interne IIterator<T>^ et T.)
begin/ end (IIterable<T>)
Platform::Collections::InputIterator<IKeyValuePair<K, V>^>

(Stocke en interne IIterator<T>^ et T.)
begin/ end (IMap<K,V>).
Platform::Collections::InputIterator<IKeyValuePair<K, V>^>

(Stocke en interne IIterator<T>^ et T.)
begin/ end (Windows::Foundation::Collections::IMapView)

Événements de modification de collection

Vector et Map prennent en charge la liaison des données dans les collections XAML en implémentant des événements qui se produisent lorsqu'un objet de collection est changé ou réinitialisé, ou lorsque l'élément d'une collection est inséré, supprimé ou modifié. Vous pouvez écrire vos propres types qui prennent en charge la liaison de données, bien que vous ne puissiez pas hériter de Map ou de Vector car ces types sont verrouillés.

Les délégués Windows::Foundation::Collections::VectorChangedEventHandler et Windows::Foundation::Collections::MapChangedEventHandler spécifient les signatures des gestionnaires d’événements pour les événements de modification de collection. La classe d’énumération publique Windows::Foundation::Collections::CollectionChange , ainsi que les classes ref Platform::Collection::Details::MapChangedEventArgs et Platform::Collections::Details::VectorChangedEventArgs stockent les arguments d’événement pour déterminer ce qui a déclenché l’événement. Les types *EventArgs sont définis dans l’espace de noms Details car vous n’avez pas besoin de les construire ou de les consommer explicitement lorsque vous utilisez Map ou Vector.

Voir aussi

Système de type
Informations de référence sur le langage C++/CX
Référence aux espaces de noms