From c40346561317397f4bc8ab23d018776f398c15b1 Mon Sep 17 00:00:00 2001 From: Raul Santos Date: Mon, 13 Feb 2023 02:19:45 +0100 Subject: [PATCH 1/3] Add C# collections documentation page - Move documentation of the Godot C# collection types to a dedicated page. - Document the differences between .NET collections and Godot collections. - Help users choose which type of collection to use in C#. --- .../scripting/c_sharp/c_sharp_collections.rst | 234 ++++++++++++++++++ .../scripting/c_sharp/c_sharp_differences.rst | 126 +--------- tutorials/scripting/c_sharp/index.rst | 1 + 3 files changed, 244 insertions(+), 117 deletions(-) create mode 100644 tutorials/scripting/c_sharp/c_sharp_collections.rst 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..932c4a54b --- /dev/null +++ b/tutorials/scripting/c_sharp/c_sharp_collections.rst @@ -0,0 +1,234 @@ +.. _doc_c_sharp_collections: + +C# collections +============== + +Choose a collection +------------------- + +The .NET base class library contains multiple collection types that can be +used to store and manipulate data. Godot also provide some collection types. + +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 specially inside in 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 appropiate .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 From 7f35d9b38e1351440000a43dfc7a8dd8f3b82d97 Mon Sep 17 00:00:00 2001 From: Raul Santos Date: Mon, 13 Feb 2023 18:52:24 +0100 Subject: [PATCH 2/3] Update tutorials/scripting/c_sharp/c_sharp_collections.rst Co-authored-by: Paul Joannon <437025+paulloz@users.noreply.github.com> --- tutorials/scripting/c_sharp/c_sharp_collections.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/scripting/c_sharp/c_sharp_collections.rst b/tutorials/scripting/c_sharp/c_sharp_collections.rst index 932c4a54b..521bf3700 100644 --- a/tutorials/scripting/c_sharp/c_sharp_collections.rst +++ b/tutorials/scripting/c_sharp/c_sharp_collections.rst @@ -13,7 +13,7 @@ The main difference between the `.NET collections Date: Tue, 21 Feb 2023 19:39:29 +0100 Subject: [PATCH 3/3] Apply suggestions from code review Co-authored-by: RedworkDE <10944644+RedworkDE@users.noreply.github.com> --- tutorials/scripting/c_sharp/c_sharp_collections.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tutorials/scripting/c_sharp/c_sharp_collections.rst b/tutorials/scripting/c_sharp/c_sharp_collections.rst index 521bf3700..5bd99fc6c 100644 --- a/tutorials/scripting/c_sharp/c_sharp_collections.rst +++ b/tutorials/scripting/c_sharp/c_sharp_collections.rst @@ -3,12 +3,13 @@ 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 .NET base class library contains multiple collection types that can be -used to store and manipulate data. Godot also provide some collection types. - 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, @@ -47,7 +48,7 @@ To choose which collection type to use for each situation, consider the followin * If yes, since Godot only supports :ref:`Variant-compatible ` types, use a Godot collection. - * If not, consider `choosing an appropiate .NET collection `_. + * If not, consider `choosing an appropriate .NET collection `_. * Do you need a Godot collection that represents a list or sequential set of data?