Array Internals
How C# arrays and collections map to std::vector and related types.
The Core Mapping
C# T[] (fixed-size, reference type, GC-managed) maps to std::vector<T>
(dynamic-size, value type by default, RAII-managed). This is the primary array type in sharp-runtime.
| Property | C# T[] | sharp-runtime std::vector<T> |
|---|---|---|
| Type category | Reference type | Value type (copyable) |
| Size | Fixed after creation | Dynamic; can grow/shrink |
| Memory | GC-managed heap | RAII (freed when vector destroyed) |
| Null | Can be null | Cannot be null (empty vector by convention) |
| Length | arr.Length | vec.size() |
| Index | arr[i] | vec[i] (same) |
| Bounds check | Always (throws IndexOutOfRangeException) | vec.at(i) throws; vec[i] does not |
System::Array Role
System::Array provides static template methods for operating on
std::vector<T>. It has a deleted constructor — you cannot create an instance.
It acts as a namespace for algorithm helpers: Sort, Copy, Resize,
IndexOf, Reverse, Clear.
include/System/Array.hpp
List<T> vs std::vector<T>
System::Collections::Generic::List<T> wraps std::vector<T>
and provides a richer API (Add, Remove, Contains, Find, ForEach, etc.) along with the
IList<T> interface. Choose between them:
- Use
std::vector<T>for simple fixed-shape data and interop with standard library - Use
List<T>when you need .NET-compatible API or pass the collection through interfaces
List<T> exposes ToVector() to extract the underlying std::vector.
Immutable Collections
Immutable collections in System::Collections::Immutable are backed by
shared_ptr<const std::container<T>>. This gives them value-copy semantics
with shared underlying storage — cheap to copy but they cannot be modified after creation.
auto arr = System::Collections::Immutable::ImmutableArray<int>::Create({1, 2, 3});
// arr is backed by shared_ptr<const std::vector<int>>
auto arr2 = arr; // cheap copy (increments refcount)
// arr2[0] is still 1
Ownership and Passing Arrays
Because std::vector<T> is a value type, passing it by value copies all elements.
Pass by reference (const std::vector<T>&) for read-only access, or by
(std::vector<T>&) for mutable access.
// Pass by const ref (no copy)
void printAll(const std::vector<int>& numbers) {
for (int n : numbers) std::cout << n << " ";
}
// Mutable ref (modifies caller's vector)
void addOne(std::vector<int>& numbers) {
for (int& n : numbers) n++;
}
// Pass by value (caller keeps original)
std::vector<int> sorted = sortCopy(numbers); // makes a copy
std::vector variable copies all the data.
To get reference semantics, use std::shared_ptr<std::vector<T>>.
Bounds Checking
C# always bounds-checks array access and throws IndexOutOfRangeException.
In C++:
vec[i]— undefined behavior if out of range (no check in release builds)vec.at(i)— throwsstd::out_of_range(notIndexOutOfRangeException)
For ported code that relies on proper bounds exceptions, use vec.at(i).
Multi-dimensional Arrays
C# multi-dimensional arrays (T[,] and T[][]) have no direct equivalent.
Use nested vectors for jagged arrays or flatten to 1D for rectangular arrays:
// C# int[,] grid = new int[rows, cols]
std::vector<int> grid(rows * cols, 0);
// access: grid[row * cols + col]
// C# int[][] jagged
std::vector<std::vector<int>> jagged(rows);