truongnam1996 truongnam1996 - 1 month ago 5
C# Question

is there in C# a method for List<T> like resize in c++ for vector<T>

When I use

resize(int newsize)
in C++ for
vector<T>
, it means that the
size
of this
vector
are set to
newsize
and the indexes run in range
[0..newsize)
. How to do the same in C# for
List<T>
?

Changing the
List<T>
property
Capacity
only changes the
Capacity
but leaves the
Count
the same, and furthermore the indexes still are in range
[0..Count)
. Help me out, please.

P.S. Imagine I have a
vector<T> tmp
with a
tmp.size() == 5
I cannot refer to
tmp[9]
, but when I then use
tmp.resize(10)
I may refer to
tmp[9]
. In C# if I have
List<T> tmp
with
tmp.Count == 5
I cannot refer to
tmp[9]
(
IndexOutOfRangeException
), but even when I set
tmp.Capacity=10
I will not be able to refer to
tmp[9]
coz of
tmp.Count
is still 5. I want to find some analogy of resize in C#.

Answer

No, but you can use extension methods to add your own. The following has the same behaviour as std::vector<T>::resize(), including the same time-complexity. The only difference is that in C++ we can define a default with void resize ( size_type sz, T c = T() ) and the way templates work means that that's fine if we call it without the default for a T that has no accessible parameterless constructor. In C# we can't do that, so instead we have to create one method with no constraint that matches the non-default-used case, and another with a where new() constraint that calls into it.

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

Now the likes of myList.Resize(23) or myList.Resize(23, myDefaultValue) will match what one expects from C++'s vector. I'd note though that sometimes where with C++ you'd have a vector of pointers, in C# you'd have a list of some reference-type. Hence in cases where the C++ T() produces a null pointer (because it's a pointer), here we're expecting it to call a parameterless constructor. For that reason you might find it closer to the behaviour you're used to to replace the second method with:

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

This has the same effect with value types (call parameterless constructor), but with reference-types, it'll fill with nulls. In which case, we can just rewrite the entire class to:

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

Note that this isn't so much about differences between std::vector<T> and List<T> as about the differences in how pointers are used in C++ and C#.

Comments