web-dev-qa-db-fra.com

y at-il en C # une méthode pour List <T> comme redimensionner en c ++ pour le vecteur <T>

Lorsque j'utilise resize(int newsize) en C++ pour vector<T>, cela signifie que la variable size de cette variable vector est définie sur newsize et que les index sont exécutés dans la plage [0..newsize). Comment faire la même chose en C # pour List<T>?
La modification de la propriété List<T> de la propriété Capacity modifie uniquement la Capacity mais laisse la même chose pour la variable Count; de plus, les index sont toujours dans la plage [0..Count). Aidez-moi, s'il vous plaît.

P.S. Imaginez que j'ai un vector<T> tmp avec un tmp.size() == 5. Je ne peux pas me référer à tmp[9], mais lorsque j'utilise ensuite tmp.resize(10), je peux me référer à tmp[9]. En C # si j'ai List<T> tmp avec tmp.Count == 5, je ne peux pas me référer à tmp[9] (IndexOutOfRangeException), mais même si j'ai défini tmp.Capacity=10, je ne pourrai pas me référer à tmp[9] coz de tmp.Count vaut toujours 5. Je veux trouver une analogie de redimensionnement en C #.

23
nhtrnm

Non, mais vous pouvez utiliser des méthodes d'extension pour ajouter les vôtres. Ce qui suit a le même comportement que std::vector<T>::resize(), y compris la même complexité temporelle. La seule différence est qu'en C++, nous pouvons définir une valeur par défaut avec void resize ( size_type sz, T c = T() ) et la façon dont fonctionnent les modèles signifie que c'est correct si nous l'appelons sans la valeur par défaut pour une T qui n'a pas de constructeur accessible sans paramètre. En C #, nous ne pouvons pas faire cela. Nous devons donc créer une méthode sans contrainte qui correspond au cas non utilisé par défaut, et une autre avec une contrainte where new() qui l'appelle.

public static class ListExtra
{
    public static void Resize<T>(this List<T> list, int sz, T c)
    {
        int cur = list.Count;
        if(sz < cur)
            list.RemoveRange(sz, cur - sz);
        else if(sz > cur)
        {
            if(sz > list.Capacity)//this bit is purely an optimisation, to avoid multiple automatic capacity changes.
              list.Capacity = sz;
            list.AddRange(Enumerable.Repeat(c, sz - cur));
        }
    }
    public static void Resize<T>(this List<T> list, int sz) where T : new()
    {
        Resize(list, sz, new T());
    }
}

Les goûts de myList.Resize(23) ou myList.Resize(23, myDefaultValue) correspondront à ce que l’on attend du vecteur de C++. Je remarquerais cependant que, parfois, avec C++, on aurait un vecteur de pointeurs, en C #, une liste de types de référence. Par conséquent, dans les cas où C++ T() génère un pointeur null (car il s’agit d’un pointeur), nous nous attendons à ce qu’il appelle un constructeur sans paramètre. Pour cette raison, vous trouverez peut-être plus proche du comportement auquel vous êtes habitué pour remplacer la deuxième méthode par:

  public static void Resize<T>(this List<T> list, int sz)
  {
      Resize(list, sz, default(T));
  }

Cela a le même effet avec les types de valeur (appel de constructeur sans paramètre), mais avec les types de référence, il se remplira de null Dans ce cas, nous pouvons simplement réécrire toute la classe pour:

public static class ListExtra
{
    public static void Resize<T>(this List<T> list, int sz, T c = default(T))
    {
        int cur = list.Count;
        if(sz < cur)
            list.RemoveRange(sz, cur - sz);
        else if(sz > cur)
            list.AddRange(Enumerable.Repeat(c, sz - cur));
    }
}

Notez qu'il ne s'agit pas tant des différences entre std::vector<T> et List<T> que des différences d'utilisation des pointeurs en C++ et en C #.

28
Jon Hanna

Juste pour rendre la réponse de Jon Hanna plus lisible:

public static class ListExtras
{
    //    list: List<T> to resize
    //    size: desired new size
    // element: default value to insert

    public static void Resize<T>(this List<T> list, int size, T element = default(T))
    {
        int count = list.Count;

        if (size < count)
        {
            list.RemoveRange(size, count - size);
        }
        else if (size > count)
        {
            if (size > list.Capacity)   // Optimization
                list.Capacity = size;

            list.AddRange(Enumerable.Repeat(element, size - count));
        }
    }
}
10
puradox

pardon. est-ce ce dont tu as besoin? List.TrimExcess ()

2
DarthVader

Ceci est ma solution.

private void listResize<T>(List<T> list, int size)
{
   if (size > list.Count)
      while (size - list.Count > 0)
         list.Add(default<T>);    
   else if (size < list.Count)
      while (list.Count - size > 0)
         list.RemoveAt(list.Count-1);
}

Lorsque size et list.Count sont identiques, il n'est pas nécessaire de redimensionner la liste.

Le paramètre default(T) est utilisé à la place de null, "", 0 ou d'autres types nullables, pour remplir un élément vide de la liste, car nous ne savons pas quel type <T> est (référence, valeur, struct, etc.).

P.S. J'ai utilisé des boucles for au lieu de while boucles et j'ai rencontré un problème. La taille de la liste n'était pas toujours celle que je demandais. Il était plus petit. Des pensées pourquoi?

Vérifie ça:

private void listResize<T>(List<T> list, int size)
{
   if (size > list.Count)
      for (int i = 0; i <= size - list.Count; i++)
         list.Add(default(T));
   else if (size < list.Count)
      for (int i = 0; i <= list.Count - size; i++)
         list.RemoveAt(list.Count-1);
}
0

Paramétrer List<T>.Capacity revient à utiliser std::vector<T>.reserve(..). Peut-être que List<T>.AddRange(..) répondra à vos besoins.

0
Simon