diff --git a/tutorials/scripting/c_sharp/c_sharp_collections.rst b/tutorials/scripting/c_sharp/c_sharp_collections.rst
new file mode 100644
index 000000000..5bd99fc6c
--- /dev/null
+++ b/tutorials/scripting/c_sharp/c_sharp_collections.rst
@@ -0,0 +1,235 @@
+.. _doc_c_sharp_collections:
+
+C# collections
+==============
+
+The .NET base class library contains multiple collection types that can be
+used to store and manipulate data. Godot also provide some collection types
+that are tightly integrated with the rest of the engine.
+
+Choose a collection
+-------------------
+
+The main difference between the `.NET collections `_
+and the Godot collections is that the .NET collections are implemented in C# while
+the Godot collections are implemented in C++ and the Godot C# API is a wrapper over it,
+this is an important distinction since it means every operation on a Godot collection
+requires marshaling which can be expensive especially inside a loop.
+
+Due to the performance implications, using Godot collections is only recommended
+when absolutely necessary (such as interacting with the Godot API). Godot only
+understands its own collection types, so it's required to use them when talking
+to the engine.
+
+If you have a collection of elements that don't need to be passed to a Godot API,
+using a .NET collection would be more performant.
+
+.. tip::
+
+ It's also possible to convert between .NET collections and Godot collections.
+ The Godot collections contain constructors from generic .NET collection interfaces
+ that copy their elements, and the Godot collections can be used with the
+ `LINQ `_
+ ``ToList``, ``ToArray`` and ``ToDictionary`` methods. But keep in mind this conversion
+ requires marshaling every element in the collection and copies it to a new collection
+ so it can be expensive.
+
+Despite this, the Godot collections are optimized to try and avoid unnecessary
+marshaling, so methods like ``Sort`` or ``Reverse`` are implemented with a single
+interop call and don't need to marshal every element. Keep an eye out for generic APIs
+that take collection interfaces like `LINQ `_
+because every method requires iterating the collection and, therefore, marshaling
+every element. Prefer using the instance methods of the Godot collections when possible.
+
+To choose which collection type to use for each situation, consider the following questions:
+
+* Does your collection need to interact with the Godot engine?
+ (e.g.: the type of an exported property, calling a Godot method).
+
+ * If yes, since Godot only supports :ref:`Variant-compatible `
+ types, use a Godot collection.
+ * If not, consider `choosing an appropriate .NET collection `_.
+
+* Do you need a Godot collection that represents a list or sequential set of data?
+
+ * Godot :ref:`arrays ` are similar to the C# collection ``List``.
+ * Godot :ref:`packed arrays ` are more memory-efficient arrays,
+ in C# use one of the supported ``System.Array`` types.
+
+* Do you need a Godot collection that maps a set of keys to a set of values?
+
+ * Godot :ref:`dictionaries ` store pairs of keys and values
+ and allow easy access to the values by their associated key.
+
+Godot collections
+-----------------
+
+.. _doc_c_sharp_collections_packedarray:
+
+PackedArray
+^^^^^^^^^^^
+
+Godot packed arrays are implemented as an array of a specific type, allowing it to be
+more tightly packed as each element has the size of the specific type, not ``Variant``.
+
+In C#, packed arrays are replaced by ``System.Array``:
+
+====================== ==============================================================
+GDScript C#
+====================== ==============================================================
+``PackedInt32Array`` ``int[]``
+``PackedInt64Array`` ``long[]``
+``PackedByteArray`` ``byte[]``
+``PackedFloat32Array`` ``float[]``
+``PackedFloat64Array`` ``double[]``
+``PackedStringArray`` ``string[]``
+``PackedColorArray`` ``Color[]``
+``PackedVector2Array`` ``Vector2[]``
+``PackedVector3Array`` ``Vector3[]``
+====================== ==============================================================
+
+Other C# arrays are not supported by the Godot C# API since a packed array equivalent
+does not exist. See :ref:`Variant ` for a list of all the compatible
+types.
+
+.. _doc_c_sharp_collections_array:
+
+Array
+^^^^^
+
+Godot arrays are implemented as an array of ``Variant`` and can contain several elements
+of any type. In C#, the equivalent type is ``Godot.Collections.Array``.
+
+The generic ``Godot.Collections.Array`` type allows restricting the element type to
+a :ref:`Variant-compatible ` type.
+
+An untyped ``Godot.Collections.Array`` can be converted to a typed array using the
+``Godot.Collections.Array(Godot.Collections.Array)`` constructor.
+
+.. note::
+
+ Despite the name, Godot arrays are more similar to the C# collection
+ ``List`` than ``System.Array``. Their size is not fixed and can grow
+ or shrink as elements are added/removed from the collection.
+
+List of Godot's Array methods and their equivalent in C#:
+
+======================= ==============================================================
+GDScript C#
+======================= ==============================================================
+all `System.Linq.Enumerable.All`_
+any `System.Linq.Enumerable.Any`_
+append Add
+append_array AddRange
+back ``Array[^1]`` or `System.Linq.Enumerable.Last`_ or `System.Linq.Enumerable.LastOrDefault`_
+bsearch BinarySearch
+bsearch_custom N/A
+clear Clear
+count `System.Linq.Enumerable.Count`_
+duplicate Duplicate
+erase Remove
+fill Fill
+filter Use `System.Linq.Enumerable.Where`_
+find IndexOf
+front ``Array[0]`` or `System.Linq.Enumerable.First`_ or `System.Linq.Enumerable.FirstOrDefault`_
+get_typed_builtin N/A
+get_typed_class_name N/A
+get_typed_script N/A
+has Contains
+hash GD.Hash
+insert Insert
+is_empty Use ``Count == 0``
+is_read_only IsReadOnly
+is_typed N/A
+make_read_only MakeReadOnly
+map `System.Linq.Enumerable.Select`_
+max Max
+min Min
+pick_random PickRandom (Consider using `System.Random`_)
+pop_at ``Array[i]`` with ``RemoveAt(i)``
+pop_back ``Array[^1]`` with ``RemoveAt(Count - 1)``
+pop_front ``Array[0]`` with ``RemoveAt(0)``
+push_back ``Insert(Count, item)``
+push_front ``Insert(0, item)``
+reduce `System.Linq.Enumerable.Aggregate`_
+remove_at RemoveAt
+resize Resize
+reverse Reverse
+rfind LastIndexOf
+shuffle Shuffle
+size Count
+slice Slice
+sort Sort
+sort_custom `System.Linq.Enumerable.OrderBy`_
+typed_assign N/A
+operator != !RecursiveEqual
+operator + operator +
+operator < N/A
+operator <= N/A
+operator == RecursiveEqual
+operator > N/A
+operator >= N/A
+operator [] Array[int] indexer
+======================= ==============================================================
+
+.. _System.Random: https://learn.microsoft.com/en-us/dotnet/api/system.random
+.. _System.Linq.Enumerable.Aggregate: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.aggregate
+.. _System.Linq.Enumerable.All: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.all
+.. _System.Linq.Enumerable.Any: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.any
+.. _System.Linq.Enumerable.Count: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.count
+.. _System.Linq.Enumerable.First: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.first
+.. _System.Linq.Enumerable.FirstOrDefault: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.firstordefault
+.. _System.Linq.Enumerable.Last: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.last
+.. _System.Linq.Enumerable.LastOrDefault: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.lastordefault
+.. _System.Linq.Enumerable.OrderBy: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.orderby
+.. _System.Linq.Enumerable.Select: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.select
+.. _System.Linq.Enumerable.Where: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.where
+
+.. _doc_c_sharp_collections_dictionary:
+
+Dictionary
+^^^^^^^^^^
+
+Godot dictionaries are implemented as a dictionary with ``Variant`` keys and values.
+In C#, the equivalent type is ``Godot.Collections.Dictionary``.
+
+The generic ``Godot.Collections.Dictionary`` type allows restricting the key
+and value types to a :ref:`Variant-compatible ` type.
+
+An untyped ``Godot.Collections.Dictionary`` can be converted to a typed dictionary using the
+``Godot.Collections.Dictionary(Godot.Collections.Dictionary)`` constructor.
+
+.. tip::
+
+ If you need a dictionary where the key is typed but not the value, use
+ ``Variant`` as the ``TValue`` generic parameter of the typed dictionary.
+
+ .. code-block:: csharp
+
+ // The keys must be string, but the values can be any Variant-compatible type.
+ var dictionary = new Godot.Collections.Dictionary();
+
+List of Godot's Dictionary methods and their equivalent in C#:
+
+======================= ==============================================================
+GDScript C#
+======================= ==============================================================
+clear Clear
+duplicate Duplicate
+erase Remove
+find_key N/A
+get Dictionary[Variant] indexer or TryGetValue
+has ContainsKey
+has_all N/A
+hash GD.Hash
+is_empty Use ``Count == 0``
+is_read_only IsReadOnly
+keys Keys
+make_read_only MakeReadOnly
+merge Merge
+size Count
+values Values
+operator != !RecursiveEqual
+operator == RecursiveEqual
+operator [] Dictionary[Variant] indexer, Add or TryGetValue
+======================= ==============================================================
diff --git a/tutorials/scripting/c_sharp/c_sharp_differences.rst b/tutorials/scripting/c_sharp/c_sharp_differences.rst
index 48d616194..d14059ed3 100644
--- a/tutorials/scripting/c_sharp/c_sharp_differences.rst
+++ b/tutorials/scripting/c_sharp/c_sharp_differences.rst
@@ -776,136 +776,28 @@ GDScript C#
Array
-----
-*This is temporary. PackedArrays will need their own types to be used the way they are meant to.*
+The equivalent of packed arrays are ``System.Array``.
-====================== ==============================================================
-GDScript C#
-====================== ==============================================================
-``Array`` ``Godot.Collections.Array``
-``PackedInt32Array`` ``int[]``
-``PackedInt64Array`` ``long[]``
-``PackedByteArray`` ``byte[]``
-``PackedFloat32Array`` ``float[]``
-``PackedFloat64Array`` ``double[]``
-``PackedStringArray`` ``string[]``
-``PackedColorArray`` ``Color[]``
-``PackedVector2Array`` ``Vector2[]``
-``PackedVector3Array`` ``Vector3[]``
-====================== ==============================================================
+See also :ref:`PackedArray in C# `.
+Use ``Godot.Collections.Array`` for an untyped ``Variant`` array.
``Godot.Collections.Array`` is a type-safe wrapper around ``Godot.Collections.Array``.
-Use the ``Godot.Collections.Array(Godot.Collections.Array)`` constructor to create one.
-List of Godot's Array methods and their equivalent in C#:
-
-======================= ==============================================================
-GDScript C#
-======================= ==============================================================
-all `System.Linq.Enumerable.All`_
-any `System.Linq.Enumerable.Any`_
-append Add
-append_array AddRange
-back ``Array[^1]`` or `System.Linq.Enumerable.Last`_ or `System.Linq.Enumerable.LastOrDefault`_
-bsearch BinarySearch
-bsearch_custom N/A
-clear Clear
-count `System.Linq.Enumerable.Count`_
-duplicate Duplicate
-erase Remove
-fill Fill
-filter Use `System.Linq.Enumerable.Where`_
-find IndexOf
-front ``Array[0]`` or `System.Linq.Enumerable.First`_ or `System.Linq.Enumerable.FirstOrDefault`_
-get_typed_builtin N/A
-get_typed_class_name N/A
-get_typed_script N/A
-has Contains
-hash GD.Hash
-insert Insert
-is_empty Use ``Count == 0``
-is_read_only IsReadOnly
-is_typed N/A
-make_read_only MakeReadOnly
-map `System.Linq.Enumerable.Select`_
-max Max
-min Min
-pick_random PickRandom (Consider using `System.Random`_)
-pop_at ``Array[i]`` with ``RemoveAt(i)``
-pop_back ``Array[^1]`` with ``RemoveAt(Count - 1)``
-pop_front ``Array[0]`` with ``RemoveAt(0)``
-push_back ``Insert(Count, item)``
-push_front ``Insert(0, item)``
-reduce `System.Linq.Enumerable.Aggregate`_
-remove_at RemoveAt
-resize Resize
-reverse Reverse
-rfind LastIndexOf
-shuffle Shuffle
-size Count
-slice Slice
-sort Sort
-sort_custom `System.Linq.Enumerable.OrderBy`_
-typed_assign N/A
-operator != !RecursiveEqual
-operator + operator +
-operator < N/A
-operator <= N/A
-operator == RecursiveEqual
-operator > N/A
-operator >= N/A
-operator [] Array[int] indexer
-======================= ==============================================================
-
-.. _System.Random: https://learn.microsoft.com/en-us/dotnet/api/system.random
-.. _System.Linq.Enumerable.Aggregate: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.aggregate
-.. _System.Linq.Enumerable.All: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.all
-.. _System.Linq.Enumerable.Any: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.any
-.. _System.Linq.Enumerable.Count: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.count
-.. _System.Linq.Enumerable.First: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.first
-.. _System.Linq.Enumerable.FirstOrDefault: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.firstordefault
-.. _System.Linq.Enumerable.Last: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.last
-.. _System.Linq.Enumerable.LastOrDefault: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.lastordefault
-.. _System.Linq.Enumerable.OrderBy: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.orderby
-.. _System.Linq.Enumerable.Select: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.select
-.. _System.Linq.Enumerable.Where: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.where
+See also :ref:`Array in C# `.
Dictionary
----------
-Use ``Godot.Collections.Dictionary``.
+Use ``Godot.Collections.Dictionary`` for an untyped ``Variant`` dictionary.
+``Godot.Colelctions.Dictionary`` is a type-safe wrapper around ``Godot.Collections.Dictionary``.
-``Godot.Collections.Dictionary`` is a type-safe wrapper around ``Godot.Collections.Dictionary``.
-Use the ``Godot.Collections.Dictionary(Godot.Collections.Dictionary)`` constructor to create one.
-
-List of Godot's Dictionary methods and their equivalent in C#:
-
-======================= ==============================================================
-GDScript C#
-======================= ==============================================================
-clear Clear
-duplicate Duplicate
-erase Remove
-find_key N/A
-get Dictionary[Variant] indexer or TryGetValue
-has ContainsKey
-has_all N/A
-hash GD.Hash
-is_empty Use ``Count == 0``
-is_read_only IsReadOnly
-keys Keys
-make_read_only MakeReadOnly
-merge Merge
-size Count
-values Values
-operator != !RecursiveEqual
-operator == RecursiveEqual
-operator [] Dictionary[Variant] indexer, Add or TryGetValue
-======================= ==============================================================
+See also :ref:`Dictionary in C# `.
Variant
-------
-``Godot.Variant`` is used to represent Godot's native :ref:`Variant ` type. Any Variant-compatible type can be converted from/to it.
+``Godot.Variant`` is used to represent Godot's native :ref:`Variant ` type.
+Any Variant-compatible type can be converted from/to it.
See also: :ref:`doc_c_sharp_variant`.
diff --git a/tutorials/scripting/c_sharp/index.rst b/tutorials/scripting/c_sharp/index.rst
index 2b512e3d9..821c7e50b 100644
--- a/tutorials/scripting/c_sharp/index.rst
+++ b/tutorials/scripting/c_sharp/index.rst
@@ -8,6 +8,7 @@ C#
c_sharp_basics
c_sharp_features
c_sharp_differences
+ c_sharp_collections
c_sharp_variant
c_sharp_signals
c_sharp_exports