From e9389ccbe6499fdeb3473b4035348583a25abfaa Mon Sep 17 00:00:00 2001 From: Raul Santos Date: Fri, 3 Feb 2023 18:22:42 +0100 Subject: [PATCH] Add a dedicated page about the C# Variant type --- .../scripting/c_sharp/c_sharp_differences.rst | 11 +- .../scripting/c_sharp/c_sharp_signals.rst | 8 +- .../scripting/c_sharp/c_sharp_variant.rst | 156 ++++++++++++++++++ tutorials/scripting/c_sharp/index.rst | 1 + 4 files changed, 160 insertions(+), 16 deletions(-) create mode 100644 tutorials/scripting/c_sharp/c_sharp_variant.rst diff --git a/tutorials/scripting/c_sharp/c_sharp_differences.rst b/tutorials/scripting/c_sharp/c_sharp_differences.rst index 4f58648df..bc08a6131 100644 --- a/tutorials/scripting/c_sharp/c_sharp_differences.rst +++ b/tutorials/scripting/c_sharp/c_sharp_differences.rst @@ -541,16 +541,9 @@ operator [] Dictionary[Variant] indexer, Add or TryGetValue Variant ------- -``Godot.Variant`` is used to represent the Godot's native :ref:`Variant ` type. Any Variant-compatible type can be converted from/to it. -We recommend avoiding ``Godot.Variant`` unless it is necessary to interact with untyped engine APIs. -Take advantage of C#'s type safety when possible. +``Godot.Variant`` is used to represent Godot's native :ref:`Variant ` type. Any Variant-compatible type can be converted from/to it. -Any of ``Variant.As{TYPE}`` methods or the generic ``Variant.As`` method can be used to convert -a ``Godot.Variant`` to a C# type. Since the ``Godot.Variant`` type contains implicit conversions -defined for all the supported types calling these methods directly is usually not necessary. - -Use ``CreateFrom`` method overloads or the generic ``From`` method to convert a C# type -to a ``Godot.Variant``. +See also: :ref:`doc_c_sharp_variant`. Communicating with other scripting languages -------------------------------------------- diff --git a/tutorials/scripting/c_sharp/c_sharp_signals.rst b/tutorials/scripting/c_sharp/c_sharp_signals.rst index 6c478b4a2..ffe73b72c 100644 --- a/tutorials/scripting/c_sharp/c_sharp_signals.rst +++ b/tutorials/scripting/c_sharp/c_sharp_signals.rst @@ -87,13 +87,7 @@ your custom signal names are listed under the nested ``SignalName`` class. In contrast with other C# events, you cannot use ``Invoke`` to raise events tied to Godot signals. -Signals support arguments of: - -* All the `built-in value types `_, - except ``decimal``, ``nint`` and ``nuint`` -* ``string`` -* Classes derived from :ref:`GodotObject ` -* Collections types defined in the ``Godot.Collections`` namespace +Signals support arguments of any :ref:`Variant-compatible ` type. Consequently, any ``Node`` or ``Reference`` will be compatible automatically, but custom data objects will need to inherit from ``GodotObject`` or one of its subclasses. diff --git a/tutorials/scripting/c_sharp/c_sharp_variant.rst b/tutorials/scripting/c_sharp/c_sharp_variant.rst new file mode 100644 index 000000000..e7a1e272d --- /dev/null +++ b/tutorials/scripting/c_sharp/c_sharp_variant.rst @@ -0,0 +1,156 @@ +.. _doc_c_sharp_variant: + +C# Variant +========== + +For a detailed explanation of Variant in general, see the :ref:`Variant ` documentation page. + +``Godot.Variant`` is used to represent Godot's native :ref:`Variant ` type. Any Variant-compatible type can be converted from/to it. +We recommend avoiding ``Godot.Variant`` unless it is necessary to interact with untyped engine APIs. +Take advantage of C#'s type safety when possible. + +Any of ``Variant.As{TYPE}`` methods or the generic ``Variant.As`` method can be used to convert +a ``Godot.Variant`` to a C# type. Since the ``Godot.Variant`` type contains implicit conversions +defined for all the supported types calling these methods directly is usually not necessary. + +Use ``CreateFrom`` method overloads or the generic ``Variant.From`` method to convert a C# type +to a ``Godot.Variant``. + +.. note:: + + Since the Variant type in C# is a struct, it can't be null. To create a "null" + Variant use the ``default`` keyword or the parameterless constructor. + +Variant-compatible types +------------------------ + +* All the `built-in value types `_, + except ``decimal``, ``nint`` and ``nuint``. +* ``string``. +* Classes derived from :ref:`GodotObject `. +* Collections types defined in the ``Godot.Collections`` namespace. + +Full list of Variant types and their equivalent C# type: + +======================= =========================================================== +Variant.Type C# Type +======================= =========================================================== +``Nil`` ``null`` (Not a type) +``Bool`` ``bool`` +``Int`` ``long`` (Godot stores 64-bit integers in Variant) +``Float`` ``double`` (Godot stores 64-bit floats in Variant) +``String`` ``string`` +``Vector2`` ``Godot.Vector2`` +``Vector2I`` ``Godot.Vector2I`` +``Rect2`` ``Godot.Rect2`` +``Rect2I`` ``Godot.Rect2I`` +``Vector3`` ``Godot.Vector3`` +``Vector3I`` ``Godot.Vector3I`` +``Transform2D`` ``Godot.Transform2D`` +``Vector4`` ``Godot.Vector4`` +``Vector4I`` ``Godot.Vector4I`` +``Plane`` ``Godot.Plane`` +``Quaternion`` ``Godot.Quaternion`` +``Aabb`` ``Godot.Aabb`` +``Basis`` ``Godot.Basis`` +``Transform3D`` ``Godot.Transform3D`` +``Projection`` ``Godot.Projection`` +``Color`` ``Godot.Color`` +``StringName`` ``Godot.StringName`` +``NodePath`` ``Godot.NodePath`` +``Rid`` ``Godot.Rid`` +``Object`` ``Godot.GodotObject`` or any derived type. +``Callable`` ``Godot.Callable`` +``Signal`` ``Godot.Signal`` +``Dictionary`` ``Godot.Collections.Dictionary`` +``Array`` ``Godot.Collections.Array`` +``PackedByteArray`` ``byte[]`` +``PackedInt32Array`` ``int[]`` +``PackedInt64Array`` ``long[]`` +``PackedFloat32Array`` ``float[]`` +``PackedFloat64Array`` ``double[]`` +``PackedStringArray`` ``string[]`` +``PackedVector2Array`` ``Godot.Vector2[]`` +``PackedVector3Array`` ``Godot.Vector3[]`` +``PackedColorArray`` ``Godot.Color[]`` +======================= =========================================================== + +.. warning:: + + Godot uses 64-bit integers and floats in Variant. Smaller integer and float types + such as ``int``, ``short`` and ``float`` are supported since they can fit in the + bigger type. Be aware that an implicit conversion is performed so using the wrong + type will result in potential precission loss. + +.. warning:: + + Enums are supported by ``Godot.Variant`` since their underlying type is an integer + type which are all compatible. However, implicit conversions don't exist, enums must + be manually converted to their underlying integer type before they can converted to/from + ``Godot.Variant`` or use the generic ``Variant.As`` and ``Variant.From`` methods + to convert them. + + .. code-block:: csharp + + enum MyEnum { A, B, C } + + Variant variant1 = (int)MyEnum.A; + MyEnum enum1 = (MyEnum)(int)variant1; + + Variant variant2 = Variant.From(MyEnum.A); + MyEnum enum2 = variant2.As(); + +Using Variant in a generic context +---------------------------------- + +When using generics, you may be interested in restricting the generic ``T`` type to be +only one of the Variant-compatible types. This can be achieved using the ``[MustBeVariant]`` +attribute. + +.. code-block:: csharp + + public void MethodThatOnlySupportsVariants<[MustBeVariant] T>(T onlyVariant) + { + // Do something with the Variant-compatible value. + } + +Combined with the generic ``Variant.From`` allows you to obtain an instance of ``Godot.Variant`` +from an instance of a generic ``T`` type. Then it can be used in any API that only supports the +``Godot.Variant`` struct. + +.. code-block:: csharp + + public void Method1<[MustBeVariant] T>(T variantCompatible) + { + Variant variant = Variant.From(variantCompatible); + Method2(variant); + } + + public void Method2(Variant variant) + { + // Do something with variant. + } + +In order to invoke a method with a generic parameter annotated with the ``[MustBeVariant]`` +attribute, the value must be a Variant-compatible type or a generic ``T`` type annotated +with the ``[MustBeVariant]`` attribute as well. + +.. code-block:: csharp + + public class ObjectDerivedClass : GodotObject { } + + public class NonObjectDerivedClass { } + + public void Main<[MustBeVariant] T1, T2>(T1 someGeneric1, T2 someGeneric2) + { + MyMethod(42); // Works because `int` is a Variant-compatible type. + MyMethod(new ObjectDerivedClass()); // Works because any type that derives from `GodotObject` is a Variant-compatible type. + MyMethod(new NonObjectDerivedClass()); // Does NOT work because the type is not Variant-compatible. + MyMethod(someGeneric1); // Works because `T1` is annotated with the `[MustBeVariant]` attribute. + MyMethod(someGeneric2); // Does NOT work because `T2` is NOT annotated with the `[MustBeVariant]` attribute. + } + + public void MyMethod<[MustBeVariant] T>(T variant) + { + // Do something with variant. + } diff --git a/tutorials/scripting/c_sharp/index.rst b/tutorials/scripting/c_sharp/index.rst index e9bde3a5a..2b512e3d9 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_variant c_sharp_signals c_sharp_exports c_sharp_style_guide