瀏覽代碼

Merge pull request #69428 from neikeq/no

C#: Cleanup of Marshaling methods
Rémi Verschelde 2 年之前
父節點
當前提交
d746b618be
共有 18 個文件被更改,包括 479 次插入971 次删除
  1. 5 4
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
  2. 2 4
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs
  3. 30 344
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
  4. 5 3
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
  5. 9 5
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
  6. 8 4
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
  7. 4 2
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
  8. 5 3
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
  9. 1 1
      modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
  10. 15 13
      modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
  11. 14 47
      modules/mono/editor/bindings_generator.cpp
  12. 7 2
      modules/mono/editor/bindings_generator.h
  13. 2 2
      modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
  14. 39 20
      modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
  15. 273 2
      modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
  16. 11 506
      modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
  17. 2 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs
  18. 47 9
      modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs

+ 5 - 4
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs

@@ -268,8 +268,9 @@ namespace Godot.SourceGenerators
             if (parameters.Length > paramTypes.Length)
                 return null; // Ignore incompatible method
 
-            return new GodotMethodData(method, paramTypes, parameters
-                .Select(p => p.Type).ToImmutableArray(), retType, retSymbol);
+            return new GodotMethodData(method, paramTypes,
+                parameters.Select(p => p.Type).ToImmutableArray(),
+                retType != null ? (retType.Value, retSymbol) : null);
         }
 
         public static IEnumerable<GodotMethodData> WhereHasGodotCompatibleSignature(
@@ -330,10 +331,10 @@ namespace Godot.SourceGenerators
 
         public static string Path(this Location location)
             => location.SourceTree?.GetLineSpan(location.SourceSpan).Path
-            ?? location.GetLineSpan().Path;
+               ?? location.GetLineSpan().Path;
 
         public static int StartLine(this Location location)
             => location.SourceTree?.GetLineSpan(location.SourceSpan).StartLinePosition.Line
-            ?? location.GetLineSpan().StartLinePosition.Line;
+               ?? location.GetLineSpan().StartLinePosition.Line;
     }
 }

+ 2 - 4
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs

@@ -6,20 +6,18 @@ namespace Godot.SourceGenerators
     public readonly struct GodotMethodData
     {
         public GodotMethodData(IMethodSymbol method, ImmutableArray<MarshalType> paramTypes,
-            ImmutableArray<ITypeSymbol> paramTypeSymbols, MarshalType? retType, ITypeSymbol? retSymbol)
+            ImmutableArray<ITypeSymbol> paramTypeSymbols, (MarshalType MarshalType, ITypeSymbol TypeSymbol)? retType)
         {
             Method = method;
             ParamTypes = paramTypes;
             ParamTypeSymbols = paramTypeSymbols;
             RetType = retType;
-            RetSymbol = retSymbol;
         }
 
         public IMethodSymbol Method { get; }
         public ImmutableArray<MarshalType> ParamTypes { get; }
         public ImmutableArray<ITypeSymbol> ParamTypeSymbols { get; }
-        public MarshalType? RetType { get; }
-        public ITypeSymbol? RetSymbol { get; }
+        public (MarshalType MarshalType, ITypeSymbol TypeSymbol)? RetType { get; }
     }
 
     public readonly struct GodotSignalDelegateData

+ 30 - 344
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs

@@ -304,240 +304,33 @@ namespace Godot.SourceGenerators
         {
             return marshalType switch
             {
-                MarshalType.Boolean =>
-                    source.Append(VariantUtils, ".ConvertToBool(", inputExpr, ")"),
-                MarshalType.Char =>
-                    source.Append("(char)", VariantUtils, ".ConvertToUInt16(", inputExpr, ")"),
-                MarshalType.SByte =>
-                    source.Append(VariantUtils, ".ConvertToInt8(", inputExpr, ")"),
-                MarshalType.Int16 =>
-                    source.Append(VariantUtils, ".ConvertToInt16(", inputExpr, ")"),
-                MarshalType.Int32 =>
-                    source.Append(VariantUtils, ".ConvertToInt32(", inputExpr, ")"),
-                MarshalType.Int64 =>
-                    source.Append(VariantUtils, ".ConvertToInt64(", inputExpr, ")"),
-                MarshalType.Byte =>
-                    source.Append(VariantUtils, ".ConvertToUInt8(", inputExpr, ")"),
-                MarshalType.UInt16 =>
-                    source.Append(VariantUtils, ".ConvertToUInt16(", inputExpr, ")"),
-                MarshalType.UInt32 =>
-                    source.Append(VariantUtils, ".ConvertToUInt32(", inputExpr, ")"),
-                MarshalType.UInt64 =>
-                    source.Append(VariantUtils, ".ConvertToUInt64(", inputExpr, ")"),
-                MarshalType.Single =>
-                    source.Append(VariantUtils, ".ConvertToFloat32(", inputExpr, ")"),
-                MarshalType.Double =>
-                    source.Append(VariantUtils, ".ConvertToFloat64(", inputExpr, ")"),
-                MarshalType.String =>
-                    source.Append(VariantUtils, ".ConvertToStringObject(", inputExpr, ")"),
-                MarshalType.Vector2 =>
-                    source.Append(VariantUtils, ".ConvertToVector2(", inputExpr, ")"),
-                MarshalType.Vector2i =>
-                    source.Append(VariantUtils, ".ConvertToVector2i(", inputExpr, ")"),
-                MarshalType.Rect2 =>
-                    source.Append(VariantUtils, ".ConvertToRect2(", inputExpr, ")"),
-                MarshalType.Rect2i =>
-                    source.Append(VariantUtils, ".ConvertToRect2i(", inputExpr, ")"),
-                MarshalType.Transform2D =>
-                    source.Append(VariantUtils, ".ConvertToTransform2D(", inputExpr, ")"),
-                MarshalType.Vector3 =>
-                    source.Append(VariantUtils, ".ConvertToVector3(", inputExpr, ")"),
-                MarshalType.Vector3i =>
-                    source.Append(VariantUtils, ".ConvertToVector3i(", inputExpr, ")"),
-                MarshalType.Basis =>
-                    source.Append(VariantUtils, ".ConvertToBasis(", inputExpr, ")"),
-                MarshalType.Quaternion =>
-                    source.Append(VariantUtils, ".ConvertToQuaternion(", inputExpr, ")"),
-                MarshalType.Transform3D =>
-                    source.Append(VariantUtils, ".ConvertToTransform3D(", inputExpr, ")"),
-                MarshalType.Vector4 =>
-                    source.Append(VariantUtils, ".ConvertToVector4(", inputExpr, ")"),
-                MarshalType.Vector4i =>
-                    source.Append(VariantUtils, ".ConvertToVector4i(", inputExpr, ")"),
-                MarshalType.Projection =>
-                    source.Append(VariantUtils, ".ConvertToProjection(", inputExpr, ")"),
-                MarshalType.AABB =>
-                    source.Append(VariantUtils, ".ConvertToAABB(", inputExpr, ")"),
-                MarshalType.Color =>
-                    source.Append(VariantUtils, ".ConvertToColor(", inputExpr, ")"),
-                MarshalType.Plane =>
-                    source.Append(VariantUtils, ".ConvertToPlane(", inputExpr, ")"),
-                MarshalType.Callable =>
-                    source.Append(VariantUtils, ".ConvertToCallableManaged(", inputExpr, ")"),
-                MarshalType.SignalInfo =>
-                    source.Append(VariantUtils, ".ConvertToSignalInfo(", inputExpr, ")"),
-                MarshalType.Enum =>
-                    source.Append("(", typeSymbol.FullQualifiedNameIncludeGlobal(),
-                        ")", VariantUtils, ".ConvertToInt32(", inputExpr, ")"),
-                MarshalType.ByteArray =>
-                    source.Append(VariantUtils, ".ConvertAsPackedByteArrayToSystemArray(", inputExpr, ")"),
-                MarshalType.Int32Array =>
-                    source.Append(VariantUtils, ".ConvertAsPackedInt32ArrayToSystemArray(", inputExpr, ")"),
-                MarshalType.Int64Array =>
-                    source.Append(VariantUtils, ".ConvertAsPackedInt64ArrayToSystemArray(", inputExpr, ")"),
-                MarshalType.Float32Array =>
-                    source.Append(VariantUtils, ".ConvertAsPackedFloat32ArrayToSystemArray(", inputExpr, ")"),
-                MarshalType.Float64Array =>
-                    source.Append(VariantUtils, ".ConvertAsPackedFloat64ArrayToSystemArray(", inputExpr, ")"),
-                MarshalType.StringArray =>
-                    source.Append(VariantUtils, ".ConvertAsPackedStringArrayToSystemArray(", inputExpr, ")"),
-                MarshalType.Vector2Array =>
-                    source.Append(VariantUtils, ".ConvertAsPackedVector2ArrayToSystemArray(", inputExpr, ")"),
-                MarshalType.Vector3Array =>
-                    source.Append(VariantUtils, ".ConvertAsPackedVector3ArrayToSystemArray(", inputExpr, ")"),
-                MarshalType.ColorArray =>
-                    source.Append(VariantUtils, ".ConvertAsPackedColorArrayToSystemArray(", inputExpr, ")"),
-                MarshalType.GodotObjectOrDerivedArray =>
-                    source.Append(VariantUtils, ".ConvertToSystemArrayOfGodotObject<",
-                        ((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
-                MarshalType.SystemArrayOfStringName =>
-                    source.Append(VariantUtils, ".ConvertToSystemArrayOfStringName(", inputExpr, ")"),
-                MarshalType.SystemArrayOfNodePath =>
-                    source.Append(VariantUtils, ".ConvertToSystemArrayOfNodePath(", inputExpr, ")"),
-                MarshalType.SystemArrayOfRID =>
-                    source.Append(VariantUtils, ".ConvertToSystemArrayOfRID(", inputExpr, ")"),
-                MarshalType.Variant =>
-                    source.Append("global::Godot.Variant.CreateCopyingBorrowed(", inputExpr, ")"),
-                MarshalType.GodotObjectOrDerived =>
-                    source.Append("(", typeSymbol.FullQualifiedNameIncludeGlobal(),
-                        ")", VariantUtils, ".ConvertToGodotObject(", inputExpr, ")"),
-                MarshalType.StringName =>
-                    source.Append(VariantUtils, ".ConvertToStringNameObject(", inputExpr, ")"),
-                MarshalType.NodePath =>
-                    source.Append(VariantUtils, ".ConvertToNodePathObject(", inputExpr, ")"),
-                MarshalType.RID =>
-                    source.Append(VariantUtils, ".ConvertToRID(", inputExpr, ")"),
-                MarshalType.GodotDictionary =>
-                    source.Append(VariantUtils, ".ConvertToDictionaryObject(", inputExpr, ")"),
-                MarshalType.GodotArray =>
-                    source.Append(VariantUtils, ".ConvertToArrayObject(", inputExpr, ")"),
+                // For generic Godot collections, VariantUtils.ConvertTo<T> is slower, so we need this special case
                 MarshalType.GodotGenericDictionary =>
                     source.Append(VariantUtils, ".ConvertToDictionaryObject<",
                         ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ",
-                        ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
+                        ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">(",
+                        inputExpr, ")"),
                 MarshalType.GodotGenericArray =>
                     source.Append(VariantUtils, ".ConvertToArrayObject<",
-                        ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
-                _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
-                    "Received unexpected marshal type")
+                        ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">(",
+                        inputExpr, ")"),
+                _ => source.Append(VariantUtils, ".ConvertTo<",
+                    typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
             };
         }
 
-        public static StringBuilder AppendManagedToNativeVariantExpr(
-            this StringBuilder source, string inputExpr, MarshalType marshalType)
+        public static StringBuilder AppendManagedToNativeVariantExpr(this StringBuilder source,
+            string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
         {
             return marshalType switch
             {
-                MarshalType.Boolean =>
-                    source.Append(VariantUtils, ".CreateFromBool(", inputExpr, ")"),
-                MarshalType.Char =>
-                    source.Append(VariantUtils, ".CreateFromInt((ushort)", inputExpr, ")"),
-                MarshalType.SByte =>
-                    source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
-                MarshalType.Int16 =>
-                    source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
-                MarshalType.Int32 =>
-                    source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
-                MarshalType.Int64 =>
-                    source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
-                MarshalType.Byte =>
-                    source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
-                MarshalType.UInt16 =>
-                    source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
-                MarshalType.UInt32 =>
-                    source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
-                MarshalType.UInt64 =>
-                    source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
-                MarshalType.Single =>
-                    source.Append(VariantUtils, ".CreateFromFloat(", inputExpr, ")"),
-                MarshalType.Double =>
-                    source.Append(VariantUtils, ".CreateFromFloat(", inputExpr, ")"),
-                MarshalType.String =>
-                    source.Append(VariantUtils, ".CreateFromString(", inputExpr, ")"),
-                MarshalType.Vector2 =>
-                    source.Append(VariantUtils, ".CreateFromVector2(", inputExpr, ")"),
-                MarshalType.Vector2i =>
-                    source.Append(VariantUtils, ".CreateFromVector2i(", inputExpr, ")"),
-                MarshalType.Rect2 =>
-                    source.Append(VariantUtils, ".CreateFromRect2(", inputExpr, ")"),
-                MarshalType.Rect2i =>
-                    source.Append(VariantUtils, ".CreateFromRect2i(", inputExpr, ")"),
-                MarshalType.Transform2D =>
-                    source.Append(VariantUtils, ".CreateFromTransform2D(", inputExpr, ")"),
-                MarshalType.Vector3 =>
-                    source.Append(VariantUtils, ".CreateFromVector3(", inputExpr, ")"),
-                MarshalType.Vector3i =>
-                    source.Append(VariantUtils, ".CreateFromVector3i(", inputExpr, ")"),
-                MarshalType.Basis =>
-                    source.Append(VariantUtils, ".CreateFromBasis(", inputExpr, ")"),
-                MarshalType.Quaternion =>
-                    source.Append(VariantUtils, ".CreateFromQuaternion(", inputExpr, ")"),
-                MarshalType.Transform3D =>
-                    source.Append(VariantUtils, ".CreateFromTransform3D(", inputExpr, ")"),
-                MarshalType.Vector4 =>
-                    source.Append(VariantUtils, ".CreateFromVector4(", inputExpr, ")"),
-                MarshalType.Vector4i =>
-                    source.Append(VariantUtils, ".CreateFromVector4i(", inputExpr, ")"),
-                MarshalType.Projection =>
-                    source.Append(VariantUtils, ".CreateFromProjection(", inputExpr, ")"),
-                MarshalType.AABB =>
-                    source.Append(VariantUtils, ".CreateFromAABB(", inputExpr, ")"),
-                MarshalType.Color =>
-                    source.Append(VariantUtils, ".CreateFromColor(", inputExpr, ")"),
-                MarshalType.Plane =>
-                    source.Append(VariantUtils, ".CreateFromPlane(", inputExpr, ")"),
-                MarshalType.Callable =>
-                    source.Append(VariantUtils, ".CreateFromCallable(", inputExpr, ")"),
-                MarshalType.SignalInfo =>
-                    source.Append(VariantUtils, ".CreateFromSignalInfo(", inputExpr, ")"),
-                MarshalType.Enum =>
-                    source.Append(VariantUtils, ".CreateFromInt((int)", inputExpr, ")"),
-                MarshalType.ByteArray =>
-                    source.Append(VariantUtils, ".CreateFromPackedByteArray(", inputExpr, ")"),
-                MarshalType.Int32Array =>
-                    source.Append(VariantUtils, ".CreateFromPackedInt32Array(", inputExpr, ")"),
-                MarshalType.Int64Array =>
-                    source.Append(VariantUtils, ".CreateFromPackedInt64Array(", inputExpr, ")"),
-                MarshalType.Float32Array =>
-                    source.Append(VariantUtils, ".CreateFromPackedFloat32Array(", inputExpr, ")"),
-                MarshalType.Float64Array =>
-                    source.Append(VariantUtils, ".CreateFromPackedFloat64Array(", inputExpr, ")"),
-                MarshalType.StringArray =>
-                    source.Append(VariantUtils, ".CreateFromPackedStringArray(", inputExpr, ")"),
-                MarshalType.Vector2Array =>
-                    source.Append(VariantUtils, ".CreateFromPackedVector2Array(", inputExpr, ")"),
-                MarshalType.Vector3Array =>
-                    source.Append(VariantUtils, ".CreateFromPackedVector3Array(", inputExpr, ")"),
-                MarshalType.ColorArray =>
-                    source.Append(VariantUtils, ".CreateFromPackedColorArray(", inputExpr, ")"),
-                MarshalType.GodotObjectOrDerivedArray =>
-                    source.Append(VariantUtils, ".CreateFromSystemArrayOfGodotObject(", inputExpr, ")"),
-                MarshalType.SystemArrayOfStringName =>
-                    source.Append(VariantUtils, ".CreateFromSystemArrayOfStringName(", inputExpr, ")"),
-                MarshalType.SystemArrayOfNodePath =>
-                    source.Append(VariantUtils, ".CreateFromSystemArrayOfNodePath(", inputExpr, ")"),
-                MarshalType.SystemArrayOfRID =>
-                    source.Append(VariantUtils, ".CreateFromSystemArrayOfRID(", inputExpr, ")"),
-                MarshalType.Variant =>
-                    source.Append(inputExpr, ".CopyNativeVariant()"),
-                MarshalType.GodotObjectOrDerived =>
-                    source.Append(VariantUtils, ".CreateFromGodotObject(", inputExpr, ")"),
-                MarshalType.StringName =>
-                    source.Append(VariantUtils, ".CreateFromStringName(", inputExpr, ")"),
-                MarshalType.NodePath =>
-                    source.Append(VariantUtils, ".CreateFromNodePath(", inputExpr, ")"),
-                MarshalType.RID =>
-                    source.Append(VariantUtils, ".CreateFromRID(", inputExpr, ")"),
-                MarshalType.GodotDictionary =>
-                    source.Append(VariantUtils, ".CreateFromDictionary(", inputExpr, ")"),
-                MarshalType.GodotArray =>
-                    source.Append(VariantUtils, ".CreateFromArray(", inputExpr, ")"),
+                // For generic Godot collections, VariantUtils.CreateFrom<T> is slower, so we need this special case
                 MarshalType.GodotGenericDictionary =>
                     source.Append(VariantUtils, ".CreateFromDictionary(", inputExpr, ")"),
                 MarshalType.GodotGenericArray =>
                     source.Append(VariantUtils, ".CreateFromArray(", inputExpr, ")"),
-                _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
-                    "Received unexpected marshal type")
+                _ => source.Append(VariantUtils, ".CreateFrom<",
+                    typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
             };
         }
 
@@ -546,137 +339,30 @@ namespace Godot.SourceGenerators
         {
             return marshalType switch
             {
-                MarshalType.Boolean => source.Append(inputExpr, ".AsBool()"),
-                MarshalType.Char => source.Append(inputExpr, ".AsChar()"),
-                MarshalType.SByte => source.Append(inputExpr, ".AsSByte()"),
-                MarshalType.Int16 => source.Append(inputExpr, ".AsInt16()"),
-                MarshalType.Int32 => source.Append(inputExpr, ".AsInt32()"),
-                MarshalType.Int64 => source.Append(inputExpr, ".AsInt64()"),
-                MarshalType.Byte => source.Append(inputExpr, ".AsByte()"),
-                MarshalType.UInt16 => source.Append(inputExpr, ".AsUInt16()"),
-                MarshalType.UInt32 => source.Append(inputExpr, ".AsUInt32()"),
-                MarshalType.UInt64 => source.Append(inputExpr, ".AsUInt64()"),
-                MarshalType.Single => source.Append(inputExpr, ".AsSingle()"),
-                MarshalType.Double => source.Append(inputExpr, ".AsDouble()"),
-                MarshalType.String => source.Append(inputExpr, ".AsString()"),
-                MarshalType.Vector2 => source.Append(inputExpr, ".AsVector2()"),
-                MarshalType.Vector2i => source.Append(inputExpr, ".AsVector2i()"),
-                MarshalType.Rect2 => source.Append(inputExpr, ".AsRect2()"),
-                MarshalType.Rect2i => source.Append(inputExpr, ".AsRect2i()"),
-                MarshalType.Transform2D => source.Append(inputExpr, ".AsTransform2D()"),
-                MarshalType.Vector3 => source.Append(inputExpr, ".AsVector3()"),
-                MarshalType.Vector3i => source.Append(inputExpr, ".AsVector3i()"),
-                MarshalType.Basis => source.Append(inputExpr, ".AsBasis()"),
-                MarshalType.Quaternion => source.Append(inputExpr, ".AsQuaternion()"),
-                MarshalType.Transform3D => source.Append(inputExpr, ".AsTransform3D()"),
-                MarshalType.Vector4 => source.Append(inputExpr, ".AsVector4()"),
-                MarshalType.Vector4i => source.Append(inputExpr, ".AsVector4i()"),
-                MarshalType.Projection => source.Append(inputExpr, ".AsProjection()"),
-                MarshalType.AABB => source.Append(inputExpr, ".AsAABB()"),
-                MarshalType.Color => source.Append(inputExpr, ".AsColor()"),
-                MarshalType.Plane => source.Append(inputExpr, ".AsPlane()"),
-                MarshalType.Callable => source.Append(inputExpr, ".AsCallable()"),
-                MarshalType.SignalInfo => source.Append(inputExpr, ".AsSignalInfo()"),
-                MarshalType.Enum =>
-                    source.Append("(", typeSymbol.FullQualifiedNameIncludeGlobal(), ")", inputExpr, ".AsInt64()"),
-                MarshalType.ByteArray => source.Append(inputExpr, ".AsByteArray()"),
-                MarshalType.Int32Array => source.Append(inputExpr, ".AsInt32Array()"),
-                MarshalType.Int64Array => source.Append(inputExpr, ".AsInt64Array()"),
-                MarshalType.Float32Array => source.Append(inputExpr, ".AsFloat32Array()"),
-                MarshalType.Float64Array => source.Append(inputExpr, ".AsFloat64Array()"),
-                MarshalType.StringArray => source.Append(inputExpr, ".AsStringArray()"),
-                MarshalType.Vector2Array => source.Append(inputExpr, ".AsVector2Array()"),
-                MarshalType.Vector3Array => source.Append(inputExpr, ".AsVector3Array()"),
-                MarshalType.ColorArray => source.Append(inputExpr, ".AsColorArray()"),
-                MarshalType.GodotObjectOrDerivedArray => source.Append(inputExpr, ".AsGodotObjectArray<",
-                    ((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedNameIncludeGlobal(), ">()"),
-                MarshalType.SystemArrayOfStringName => source.Append(inputExpr, ".AsSystemArrayOfStringName()"),
-                MarshalType.SystemArrayOfNodePath => source.Append(inputExpr, ".AsSystemArrayOfNodePath()"),
-                MarshalType.SystemArrayOfRID => source.Append(inputExpr, ".AsSystemArrayOfRID()"),
-                MarshalType.Variant => source.Append(inputExpr),
-                MarshalType.GodotObjectOrDerived => source.Append("(",
-                    typeSymbol.FullQualifiedNameIncludeGlobal(), ")", inputExpr, ".AsGodotObject()"),
-                MarshalType.StringName => source.Append(inputExpr, ".AsStringName()"),
-                MarshalType.NodePath => source.Append(inputExpr, ".AsNodePath()"),
-                MarshalType.RID => source.Append(inputExpr, ".AsRID()"),
-                MarshalType.GodotDictionary => source.Append(inputExpr, ".AsGodotDictionary()"),
-                MarshalType.GodotArray => source.Append(inputExpr, ".AsGodotArray()"),
-                MarshalType.GodotGenericDictionary => source.Append(inputExpr, ".AsGodotDictionary<",
-                    ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ",
-                    ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">()"),
-                MarshalType.GodotGenericArray => source.Append(inputExpr, ".AsGodotArray<",
-                    ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">()"),
-                _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
-                    "Received unexpected marshal type")
+                // For generic Godot collections, Variant.As<T> is slower, so we need this special case
+                MarshalType.GodotGenericDictionary =>
+                    source.Append(inputExpr, ".AsGodotDictionary<",
+                        ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ",
+                        ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">()"),
+                MarshalType.GodotGenericArray =>
+                    source.Append(inputExpr, ".AsGodotArray<",
+                        ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">()"),
+                _ => source.Append(inputExpr, ".As<",
+                    typeSymbol.FullQualifiedNameIncludeGlobal(), ">()")
             };
         }
 
         public static StringBuilder AppendManagedToVariantExpr(this StringBuilder source,
-            string inputExpr, MarshalType marshalType)
+            string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
         {
-            switch (marshalType)
+            return marshalType switch
             {
-                case MarshalType.Boolean:
-                case MarshalType.Char:
-                case MarshalType.SByte:
-                case MarshalType.Int16:
-                case MarshalType.Int32:
-                case MarshalType.Int64:
-                case MarshalType.Byte:
-                case MarshalType.UInt16:
-                case MarshalType.UInt32:
-                case MarshalType.UInt64:
-                case MarshalType.Single:
-                case MarshalType.Double:
-                case MarshalType.String:
-                case MarshalType.Vector2:
-                case MarshalType.Vector2i:
-                case MarshalType.Rect2:
-                case MarshalType.Rect2i:
-                case MarshalType.Transform2D:
-                case MarshalType.Vector3:
-                case MarshalType.Vector3i:
-                case MarshalType.Basis:
-                case MarshalType.Quaternion:
-                case MarshalType.Transform3D:
-                case MarshalType.Vector4:
-                case MarshalType.Vector4i:
-                case MarshalType.Projection:
-                case MarshalType.AABB:
-                case MarshalType.Color:
-                case MarshalType.Plane:
-                case MarshalType.Callable:
-                case MarshalType.SignalInfo:
-                case MarshalType.ByteArray:
-                case MarshalType.Int32Array:
-                case MarshalType.Int64Array:
-                case MarshalType.Float32Array:
-                case MarshalType.Float64Array:
-                case MarshalType.StringArray:
-                case MarshalType.Vector2Array:
-                case MarshalType.Vector3Array:
-                case MarshalType.ColorArray:
-                case MarshalType.GodotObjectOrDerivedArray:
-                case MarshalType.SystemArrayOfStringName:
-                case MarshalType.SystemArrayOfNodePath:
-                case MarshalType.SystemArrayOfRID:
-                case MarshalType.GodotObjectOrDerived:
-                case MarshalType.StringName:
-                case MarshalType.NodePath:
-                case MarshalType.RID:
-                case MarshalType.GodotDictionary:
-                case MarshalType.GodotArray:
-                case MarshalType.GodotGenericDictionary:
-                case MarshalType.GodotGenericArray:
-                    return source.Append("Variant.CreateFrom(", inputExpr, ")");
-                case MarshalType.Enum:
-                    return source.Append("Variant.CreateFrom((long)", inputExpr, ")");
-                case MarshalType.Variant:
-                    return source.Append(inputExpr);
-                default:
-                    throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
-                        "Received unexpected marshal type");
-            }
+                // For generic Godot collections, Variant.From<T> is slower, so we need this special case
+                MarshalType.GodotGenericDictionary or MarshalType.GodotGenericArray =>
+                    source.Append("global::Godot.Variant.CreateFrom(", inputExpr, ")"),
+                _ => source.Append("global::Godot.Variant.From<",
+                    typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")")
+            };
         }
     }
 }

+ 5 - 3
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs

@@ -135,7 +135,8 @@ namespace Godot.SourceGenerators
 
             source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
 
-            source.Append($"    public new class MethodName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.MethodName {{\n");
+            source.Append(
+                $"    public new class MethodName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.MethodName {{\n");
 
             // Generate cached StringNames for methods and properties, for fast lookup
 
@@ -297,7 +298,7 @@ namespace Godot.SourceGenerators
 
             if (method.RetType != null)
             {
-                returnVal = DeterminePropertyInfo(method.RetType.Value, name: string.Empty);
+                returnVal = DeterminePropertyInfo(method.RetType.Value.MarshalType, name: string.Empty);
             }
             else
             {
@@ -391,7 +392,8 @@ namespace Godot.SourceGenerators
             {
                 source.Append("            ret = ");
 
-                source.AppendManagedToNativeVariantExpr("callRet", method.RetType.Value);
+                source.AppendManagedToNativeVariantExpr("callRet",
+                    method.RetType.Value.TypeSymbol, method.RetType.Value.MarshalType);
                 source.Append(";\n");
 
                 source.Append("            return true;\n");

+ 9 - 5
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs

@@ -124,7 +124,8 @@ namespace Godot.SourceGenerators
 
             source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
 
-            source.Append($"    public new class PropertyName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.PropertyName {{\n");
+            source.Append(
+                $"    public new class PropertyName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.PropertyName {{\n");
 
             // Generate cached StringNames for methods and properties, for fast lookup
 
@@ -199,14 +200,14 @@ namespace Godot.SourceGenerators
                 foreach (var property in godotClassProperties)
                 {
                     GeneratePropertyGetter(property.PropertySymbol.Name,
-                        property.Type, source, isFirstEntry);
+                        property.PropertySymbol.Type, property.Type, source, isFirstEntry);
                     isFirstEntry = false;
                 }
 
                 foreach (var field in godotClassFields)
                 {
                     GeneratePropertyGetter(field.FieldSymbol.Name,
-                        field.Type, source, isFirstEntry);
+                        field.FieldSymbol.Type, field.Type, source, isFirstEntry);
                     isFirstEntry = false;
                 }
 
@@ -303,6 +304,7 @@ namespace Godot.SourceGenerators
 
         private static void GeneratePropertyGetter(
             string propertyMemberName,
+            ITypeSymbol propertyTypeSymbol,
             MarshalType propertyMarshalType,
             StringBuilder source,
             bool isFirstEntry
@@ -317,7 +319,8 @@ namespace Godot.SourceGenerators
                 .Append(propertyMemberName)
                 .Append(") {\n")
                 .Append("            value = ")
-                .AppendManagedToNativeVariantExpr("this." + propertyMemberName, propertyMarshalType)
+                .AppendManagedToNativeVariantExpr("this." + propertyMemberName,
+                    propertyTypeSymbol, propertyMarshalType)
                 .Append(";\n")
                 .Append("            return true;\n")
                 .Append("        }\n");
@@ -376,7 +379,8 @@ namespace Godot.SourceGenerators
                     if (propertyUsage != PropertyUsageFlags.Category && attr.ConstructorArguments.Length > 1)
                         hintString = attr.ConstructorArguments[1].Value?.ToString();
 
-                    yield return new PropertyInfo(VariantType.Nil, name, PropertyHint.None, hintString, propertyUsage.Value, true);
+                    yield return new PropertyInfo(VariantType.Nil, name, PropertyHint.None, hintString,
+                        propertyUsage.Value, true);
                 }
             }
         }

+ 8 - 4
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs

@@ -174,7 +174,8 @@ namespace Godot.SourceGenerators
                     }
                     else
                     {
-                        var propertyGet = propertyDeclarationSyntax.AccessorList?.Accessors.Where(a => a.Keyword.IsKind(SyntaxKind.GetKeyword)).FirstOrDefault();
+                        var propertyGet = propertyDeclarationSyntax.AccessorList?.Accessors
+                            .Where(a => a.Keyword.IsKind(SyntaxKind.GetKeyword)).FirstOrDefault();
                         if (propertyGet != null)
                         {
                             if (propertyGet.ExpressionBody != null)
@@ -200,7 +201,8 @@ namespace Godot.SourceGenerators
                             {
                                 var returns = propertyGet.DescendantNodes().OfType<ReturnStatementSyntax>();
                                 if (returns.Count() == 1)
-                                {// Generate only single return
+                                {
+                                    // Generate only single return
                                     var returnStatementSyntax = returns.Single();
                                     if (returnStatementSyntax.Expression is IdentifierNameSyntax identifierNameSyntax)
                                     {
@@ -277,7 +279,8 @@ namespace Godot.SourceGenerators
             {
                 source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
 
-                string dictionaryType = "System.Collections.Generic.Dictionary<Godot.StringName, object>";
+                string dictionaryType =
+                    "global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>";
 
                 source.Append("#if TOOLS\n");
                 source.Append("    internal new static ");
@@ -304,7 +307,8 @@ namespace Godot.SourceGenerators
                     source.Append("        values.Add(PropertyName.");
                     source.Append(exportedMember.Name);
                     source.Append(", ");
-                    source.Append(defaultValueLocalName);
+                    source.AppendManagedToVariantExpr(defaultValueLocalName,
+                        exportedMember.TypeSymbol, exportedMember.Type);
                     source.Append(");\n");
                 }
 

+ 4 - 2
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs

@@ -162,7 +162,8 @@ namespace Godot.SourceGenerators
                 source.Append("        info.AddProperty(PropertyName.")
                     .Append(propertyName)
                     .Append(", ")
-                    .AppendManagedToVariantExpr(string.Concat("this.", propertyName), property.Type)
+                    .AppendManagedToVariantExpr(string.Concat("this.", propertyName),
+                        property.PropertySymbol.Type, property.Type)
                     .Append(");\n");
             }
 
@@ -175,7 +176,8 @@ namespace Godot.SourceGenerators
                 source.Append("        info.AddProperty(PropertyName.")
                     .Append(fieldName)
                     .Append(", ")
-                    .AppendManagedToVariantExpr(string.Concat("this.", fieldName), field.Type)
+                    .AppendManagedToVariantExpr(string.Concat("this.", fieldName),
+                        field.FieldSymbol.Type, field.Type)
                     .Append(");\n");
             }
 

+ 5 - 3
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs

@@ -176,7 +176,8 @@ namespace Godot.SourceGenerators
 
             source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
 
-            source.Append($"    public new class SignalName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.SignalName {{\n");
+            source.Append(
+                $"    public new class SignalName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.SignalName {{\n");
 
             // Generate cached StringNames for methods and properties, for fast lookup
 
@@ -236,7 +237,8 @@ namespace Godot.SourceGenerators
                     .Append(signalName)
                     .Append(";\n");
 
-                source.Append($"    /// <inheritdoc cref=\"{signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal()}\"/>\n");
+                source.Append(
+                    $"    /// <inheritdoc cref=\"{signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal()}\"/>\n");
 
                 source.Append("    public event ")
                     .Append(signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal())
@@ -351,7 +353,7 @@ namespace Godot.SourceGenerators
 
             if (invokeMethodData.RetType != null)
             {
-                returnVal = DeterminePropertyInfo(invokeMethodData.RetType.Value, name: string.Empty);
+                returnVal = DeterminePropertyInfo(invokeMethodData.RetType.Value.MarshalType, name: string.Empty);
             }
             else
             {

+ 1 - 1
modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs

@@ -455,7 +455,7 @@ namespace GodotTools
             _menuPopup.IdPressed += _MenuOptionPressed;
 
             // External editor settings
-            EditorDef("mono/editor/external_editor", ExternalEditorId.None);
+            EditorDef("mono/editor/external_editor", Variant.From(ExternalEditorId.None));
 
             string settingsHintStr = "Disabled";
 

+ 15 - 13
modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs

@@ -1,3 +1,4 @@
+using Godot;
 using Godot.NativeInterop;
 using System.Diagnostics.CodeAnalysis;
 using System.Runtime.CompilerServices;
@@ -8,30 +9,31 @@ namespace GodotTools.Internals
     {
         public static float EditorScale => Internal.godot_icall_Globals_EditorScale();
 
-        public static unsafe object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false)
+        // ReSharper disable once UnusedMethodReturnValue.Global
+        public static Variant GlobalDef(string setting, Variant defaultValue, bool restartIfChanged = false)
         {
             using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
-            using godot_variant defaultValueIn = Marshaling.ConvertManagedObjectToVariant(defaultValue);
-            Internal.godot_icall_Globals_GlobalDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result);
-            using (result)
-                return Marshaling.ConvertVariantToManagedObject(result);
+            using godot_variant defaultValueIn = defaultValue.CopyNativeVariant();
+            Internal.godot_icall_Globals_GlobalDef(settingIn, defaultValueIn, restartIfChanged,
+                out godot_variant result);
+            return Variant.CreateTakingOwnershipOfDisposableValue(result);
         }
 
-        public static unsafe object EditorDef(string setting, object defaultValue, bool restartIfChanged = false)
+        // ReSharper disable once UnusedMethodReturnValue.Global
+        public static Variant EditorDef(string setting, Variant defaultValue, bool restartIfChanged = false)
         {
             using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
-            using godot_variant defaultValueIn = Marshaling.ConvertManagedObjectToVariant(defaultValue);
-            Internal.godot_icall_Globals_EditorDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result);
-            using (result)
-                return Marshaling.ConvertVariantToManagedObject(result);
+            using godot_variant defaultValueIn = defaultValue.CopyNativeVariant();
+            Internal.godot_icall_Globals_EditorDef(settingIn, defaultValueIn, restartIfChanged,
+                out godot_variant result);
+            return Variant.CreateTakingOwnershipOfDisposableValue(result);
         }
 
-        public static object EditorShortcut(string setting)
+        public static Variant EditorShortcut(string setting)
         {
             using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
             Internal.godot_icall_Globals_EditorShortcut(settingIn, out godot_variant result);
-            using (result)
-                return Marshaling.ConvertVariantToManagedObject(result);
+            return Variant.CreateTakingOwnershipOfDisposableValue(result);
         }
 
         [SuppressMessage("ReSharper", "InconsistentNaming")]

+ 14 - 47
modules/mono/editor/bindings_generator.cpp

@@ -2837,9 +2837,6 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
 		itype.is_ref_counted = ClassDB::is_parent_class(type_cname, name_cache.type_RefCounted);
 		itype.memory_own = itype.is_ref_counted;
 
-		itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToGodotObject(%0)";
-		itype.cs_managed_to_variant = "VariantUtils.CreateFromGodotObject(%0)";
-
 		itype.c_out = "%5return ";
 		itype.c_out += C_METHOD_UNMANAGED_GET_MANAGED;
 		itype.c_out += itype.is_ref_counted ? "(%1.Reference);\n" : "(%1);\n";
@@ -3218,8 +3215,6 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
 			enum_itype.cname = StringName(enum_itype.name);
 			enum_itype.proxy_name = itype.proxy_name + "." + enum_proxy_name;
 			TypeInterface::postsetup_enum_type(enum_itype);
-			enum_itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToInt32(%0)";
-			enum_itype.cs_managed_to_variant = "VariantUtils.CreateFromInt((int)%0)";
 			enum_types.insert(enum_itype.cname, enum_itype);
 		}
 
@@ -3448,16 +3443,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 
 	TypeInterface itype;
 
-#define INSERT_STRUCT_TYPE(m_type)                                     \
-	{                                                                  \
-		itype = TypeInterface::create_value_type(String(#m_type));     \
-		itype.c_type_in = #m_type "*";                                 \
-		itype.c_type_out = itype.cs_type;                              \
-		itype.cs_in_expr = "&%0";                                      \
-		itype.cs_in_expr_is_unsafe = true;                             \
-		itype.cs_variant_to_managed = "VariantUtils.ConvertTo%2(%0)";  \
-		itype.cs_managed_to_variant = "VariantUtils.CreateFrom%2(%0)"; \
-		builtin_types.insert(itype.cname, itype);                      \
+#define INSERT_STRUCT_TYPE(m_type)                                 \
+	{                                                              \
+		itype = TypeInterface::create_value_type(String(#m_type)); \
+		itype.c_type_in = #m_type "*";                             \
+		itype.c_type_out = itype.cs_type;                          \
+		itype.cs_in_expr = "&%0";                                  \
+		itype.cs_in_expr_is_unsafe = true;                         \
+		builtin_types.insert(itype.cname, itype);                  \
 	}
 
 	INSERT_STRUCT_TYPE(Vector2)
@@ -3488,8 +3481,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 	itype.c_type_out = itype.c_type;
 	itype.c_arg_in = "&%s";
 	itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromBool(%1);\n";
-	itype.cs_variant_to_managed = "VariantUtils.ConvertToBool(%0)";
-	itype.cs_managed_to_variant = "VariantUtils.CreateFromBool(%0)";
 	builtin_types.insert(itype.cname, itype);
 
 	// Integer types
@@ -3510,8 +3501,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 		itype.c_type_in = itype.name;                                                          \
 		itype.c_type_out = itype.name;                                                         \
 		itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromInt(%1);\n"; \
-		itype.cs_variant_to_managed = "VariantUtils.ConvertTo" m_int_struct_name "(%0)";       \
-		itype.cs_managed_to_variant = "VariantUtils.CreateFromInt(%0)";                        \
 		builtin_types.insert(itype.cname, itype);                                              \
 	}
 
@@ -3547,8 +3536,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 		itype.c_type_in = itype.proxy_name;
 		itype.c_type_out = itype.proxy_name;
 		itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1);\n";
-		itype.cs_variant_to_managed = "VariantUtils.ConvertToFloat32(%0)";
-		itype.cs_managed_to_variant = "VariantUtils.CreateFromFloat(%0)";
 		builtin_types.insert(itype.cname, itype);
 
 		// double
@@ -3562,8 +3549,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 		itype.c_type_in = itype.proxy_name;
 		itype.c_type_out = itype.proxy_name;
 		itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1);\n";
-		itype.cs_variant_to_managed = "VariantUtils.ConvertToFloat64(%0)";
-		itype.cs_managed_to_variant = "VariantUtils.CreateFromFloat(%0)";
 		builtin_types.insert(itype.cname, itype);
 	}
 
@@ -3581,8 +3566,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 	itype.c_type_out = itype.cs_type;
 	itype.c_type_is_disposable_struct = true;
 	itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromString(%1);\n";
-	itype.cs_variant_to_managed = "VariantUtils.ConvertToStringObject(%0)";
-	itype.cs_managed_to_variant = "VariantUtils.CreateFromString(%0)";
 	builtin_types.insert(itype.cname, itype);
 
 	// StringName
@@ -3601,8 +3584,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 	itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromStringName(%1);\n";
 	itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
 	itype.c_ret_needs_default_initialization = true;
-	itype.cs_variant_to_managed = "VariantUtils.ConvertToStringNameObject(%0)";
-	itype.cs_managed_to_variant = "VariantUtils.CreateFromStringName(%0)";
 	builtin_types.insert(itype.cname, itype);
 
 	// NodePath
@@ -3620,8 +3601,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 	itype.c_type_out = itype.cs_type;
 	itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
 	itype.c_ret_needs_default_initialization = true;
-	itype.cs_variant_to_managed = "VariantUtils.ConvertToNodePathObject(%0)";
-	itype.cs_managed_to_variant = "VariantUtils.CreateFromNodePath(%0)";
 	builtin_types.insert(itype.cname, itype);
 
 	// RID
@@ -3634,8 +3613,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 	itype.c_type = itype.cs_type;
 	itype.c_type_in = itype.c_type;
 	itype.c_type_out = itype.c_type;
-	itype.cs_variant_to_managed = "VariantUtils.ConvertToRID(%0)";
-	itype.cs_managed_to_variant = "VariantUtils.CreateFromRID(%0)";
 	builtin_types.insert(itype.cname, itype);
 
 	// Variant
@@ -3652,8 +3629,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 	itype.c_type_out = itype.cs_type;
 	itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
 	itype.c_ret_needs_default_initialization = true;
-	itype.cs_variant_to_managed = "Variant.CreateCopyingBorrowed(%0)";
-	itype.cs_managed_to_variant = "%0.CopyNativeVariant()";
 	builtin_types.insert(itype.cname, itype);
 
 	// Callable
@@ -3666,8 +3641,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 	itype.c_type_in = "in " + itype.cs_type;
 	itype.c_type_out = itype.cs_type;
 	itype.c_type_is_disposable_struct = true;
-	itype.cs_variant_to_managed = "VariantUtils.ConvertToCallableManaged(%0)";
-	itype.cs_managed_to_variant = "VariantUtils.CreateFromCallable(%0)";
 	builtin_types.insert(itype.cname, itype);
 
 	// Signal
@@ -3684,8 +3657,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 	itype.c_type_in = "in " + itype.cs_type;
 	itype.c_type_out = itype.cs_type;
 	itype.c_type_is_disposable_struct = true;
-	itype.cs_variant_to_managed = "VariantUtils.ConvertToSignalInfo(%0)";
-	itype.cs_managed_to_variant = "VariantUtils.CreateFromSignalInfo(%0)";
 	builtin_types.insert(itype.cname, itype);
 
 	// VarArg (fictitious type to represent variable arguments)
@@ -3715,8 +3686,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 		itype.c_type_in = itype.proxy_name;                                         \
 		itype.c_type_out = itype.proxy_name;                                        \
 		itype.c_type_is_disposable_struct = true;                                   \
-		itype.cs_variant_to_managed = "VariantUtils.ConvertAs%2ToSystemArray(%0)";  \
-		itype.cs_managed_to_variant = "VariantUtils.CreateFrom%2(%0)";              \
 		builtin_types.insert(itype.name, itype);                                    \
 	}
 
@@ -3752,8 +3721,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 	itype.c_type_out = itype.cs_type;
 	itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
 	itype.c_ret_needs_default_initialization = true;
-	itype.cs_variant_to_managed = "VariantUtils.ConvertToArrayObject(%0)";
-	itype.cs_managed_to_variant = "VariantUtils.CreateFromArray(%0)";
 	builtin_types.insert(itype.cname, itype);
 
 	// Array_@generic
@@ -3761,6 +3728,9 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 	itype.name = "Array_@generic";
 	itype.cname = itype.name;
 	itype.cs_out = "%5return new %2(%0(%1));";
+	// For generic Godot collections, Variant.From<T>/As<T> is slower, so we need this special case
+	itype.cs_variant_to_managed = "VariantUtils.ConvertToArrayObject(%0)";
+	itype.cs_managed_to_variant = "VariantUtils.CreateFromArray(%0)";
 	builtin_types.insert(itype.cname, itype);
 
 	// Dictionary
@@ -3778,8 +3748,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 	itype.c_type_out = itype.cs_type;
 	itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
 	itype.c_ret_needs_default_initialization = true;
-	itype.cs_variant_to_managed = "VariantUtils.ConvertToDictionaryObject(%0)";
-	itype.cs_managed_to_variant = "VariantUtils.CreateFromDictionary(%0)";
 	builtin_types.insert(itype.cname, itype);
 
 	// Dictionary_@generic
@@ -3787,6 +3755,9 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 	itype.name = "Dictionary_@generic";
 	itype.cname = itype.name;
 	itype.cs_out = "%5return new %2(%0(%1));";
+	// For generic Godot collections, Variant.From<T>/As<T> is slower, so we need this special case
+	itype.cs_variant_to_managed = "VariantUtils.ConvertToDictionaryObject(%0)";
+	itype.cs_managed_to_variant = "VariantUtils.CreateFromDictionary(%0)";
 	builtin_types.insert(itype.cname, itype);
 
 	// void (fictitious type to represent the return type of methods that do not return anything)
@@ -3852,8 +3823,6 @@ void BindingsGenerator::_populate_global_constants() {
 			enum_itype.cname = ienum.cname;
 			enum_itype.proxy_name = enum_itype.name;
 			TypeInterface::postsetup_enum_type(enum_itype);
-			enum_itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToInt32(%0)";
-			enum_itype.cs_managed_to_variant = "VariantUtils.CreateFromInt((int)%0)";
 			enum_types.insert(enum_itype.cname, enum_itype);
 
 			int prefix_length = _determine_enum_prefix(ienum);
@@ -3886,8 +3855,6 @@ void BindingsGenerator::_populate_global_constants() {
 		enum_itype.cname = enum_cname;
 		enum_itype.proxy_name = enum_itype.name;
 		TypeInterface::postsetup_enum_type(enum_itype);
-		enum_itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToInt32(%0)";
-		enum_itype.cs_managed_to_variant = "VariantUtils.CreateFromInt((int)%0)";
 		enum_types.insert(enum_itype.cname, enum_itype);
 	}
 }

+ 7 - 2
modules/mono/editor/bindings_generator.h

@@ -209,7 +209,7 @@ class BindingsGenerator {
 		String name;
 		StringName cname;
 
-		int type_parameter_count;
+		int type_parameter_count = 0;
 
 		/**
 		 * Identifier name of the base class.
@@ -514,7 +514,12 @@ class BindingsGenerator {
 
 		static void postsetup_enum_type(TypeInterface &r_enum_itype);
 
-		TypeInterface() {}
+		TypeInterface() {
+			static String default_cs_variant_to_managed = "VariantUtils.ConvertTo<%1>(%0)";
+			static String default_cs_managed_to_variant = "VariantUtils.CreateFrom<%1>(%0)";
+			cs_variant_to_managed = default_cs_variant_to_managed;
+			cs_managed_to_variant = default_cs_managed_to_variant;
+		}
 	};
 
 	struct InternalCall {

+ 2 - 2
modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs

@@ -418,8 +418,8 @@ namespace Godot.Collections
             {
                 for (int i = 0; i < count; i++)
                 {
-                    object obj = Marshaling.ConvertVariantToManagedObject(NativeValue.DangerousSelfRef.Elements[i]);
-                    array.SetValue(obj, index);
+                    object boxedVariant = Variant.CreateCopyingBorrowed(NativeValue.DangerousSelfRef.Elements[i]);
+                    array.SetValue(boxedVariant, index);
                     index++;
                 }
             }

+ 39 - 20
modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs

@@ -159,7 +159,7 @@ namespace Godot.Bridge
 
                 for (int i = 0; i < paramCount; i++)
                 {
-                    invokeParams[i] = Marshaling.ConvertVariantToManagedObjectOfType(
+                    invokeParams[i] = DelegateUtils.RuntimeTypeConversionHelper.ConvertToObjectOfType(
                         *args[i], parameters[i].ParameterType);
                 }
 
@@ -832,7 +832,8 @@ namespace Godot.Bridge
                 }
                 else
                 {
-                    interopProperties = ((godotsharp_property_info*)NativeMemory.Alloc((nuint)length, (nuint)sizeof(godotsharp_property_info)))!;
+                    interopProperties = ((godotsharp_property_info*)NativeMemory.Alloc(
+                        (nuint)length, (nuint)sizeof(godotsharp_property_info)))!;
                 }
 
                 try
@@ -858,8 +859,8 @@ namespace Godot.Bridge
 
                     addPropInfoFunc(scriptPtr, &currentClassName, interopProperties, length);
 
-                    // We're borrowing the StringName's without making an owning copy, so the
-                    // managed collection needs to be kept alive until `addPropInfoFunc` returns.
+                    // We're borrowing the native value of the StringName entries.
+                    // The dictionary needs to be kept alive until `addPropInfoFunc` returns.
                     GC.KeepAlive(properties);
                 }
                 finally
@@ -884,12 +885,7 @@ namespace Godot.Bridge
         {
             // Careful with padding...
             public godot_string_name Name; // Not owned
-            public godot_variant Value;
-
-            public void Dispose()
-            {
-                Value.Dispose();
-            }
+            public godot_variant Value; // Not owned
         }
 
         [UnmanagedCallersOnly]
@@ -928,10 +924,35 @@ namespace Godot.Bridge
                 if (getGodotPropertyDefaultValuesMethod == null)
                     return;
 
-                var defaultValues = (Dictionary<StringName, object>?)
-                    getGodotPropertyDefaultValuesMethod.Invoke(null, null);
+                var defaultValuesObj = getGodotPropertyDefaultValuesMethod.Invoke(null, null);
+
+                if (defaultValuesObj == null)
+                    return;
+
+                Dictionary<StringName, Variant> defaultValues;
+
+                if (defaultValuesObj is Dictionary<StringName, object> defaultValuesLegacy)
+                {
+                    // We have to support this for some time, otherwise this could cause data loss for projects
+                    // built with previous releases. Ideally, we should remove this before Godot 4.0 stable.
+
+                    if (defaultValuesLegacy.Count <= 0)
+                        return;
 
-                if (defaultValues == null || defaultValues.Count <= 0)
+                    defaultValues = new();
+
+                    foreach (var pair in defaultValuesLegacy)
+                    {
+                        defaultValues[pair.Key] = Variant.CreateTakingOwnershipOfDisposableValue(
+                            DelegateUtils.RuntimeTypeConversionHelper.ConvertToVariant(pair.Value));
+                    }
+                }
+                else
+                {
+                    defaultValues = (Dictionary<StringName, Variant>)defaultValuesObj;
+                }
+
+                if (defaultValues.Count <= 0)
                     return;
 
                 int length = defaultValues.Count;
@@ -952,7 +973,8 @@ namespace Godot.Bridge
                 }
                 else
                 {
-                    interopDefaultValues = ((godotsharp_property_def_val_pair*)NativeMemory.Alloc((nuint)length, (nuint)sizeof(godotsharp_property_def_val_pair)))!;
+                    interopDefaultValues = ((godotsharp_property_def_val_pair*)NativeMemory.Alloc(
+                        (nuint)length, (nuint)sizeof(godotsharp_property_def_val_pair)))!;
                 }
 
                 try
@@ -963,7 +985,7 @@ namespace Godot.Bridge
                         godotsharp_property_def_val_pair interopProperty = new()
                         {
                             Name = (godot_string_name)defaultValuePair.Key.NativeValue, // Not owned
-                            Value = Marshaling.ConvertManagedObjectToVariant(defaultValuePair.Value)
+                            Value = (godot_variant)defaultValuePair.Value.NativeVar // Not owned
                         };
 
                         interopDefaultValues[i] = interopProperty;
@@ -973,15 +995,12 @@ namespace Godot.Bridge
 
                     addDefValFunc(scriptPtr, interopDefaultValues, length);
 
-                    // We're borrowing the StringName's without making an owning copy, so the
-                    // managed collection needs to be kept alive until `addDefValFunc` returns.
+                    // We're borrowing the native value of the StringName and Variant entries.
+                    // The dictionary needs to be kept alive until `addDefValFunc` returns.
                     GC.KeepAlive(defaultValues);
                 }
                 finally
                 {
-                    for (int i = 0; i < length; i++)
-                        interopDefaultValues[i].Dispose();
-
                     if (!useStack)
                         NativeMemory.Free(interopDefaultValues);
                 }

+ 273 - 2
modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs

@@ -186,7 +186,7 @@ namespace Godot
                                 writer.Write(field.Name);
 
                                 var fieldValue = field.GetValue(target);
-                                using var fieldValueVariant = Marshaling.ConvertManagedObjectToVariant(fieldValue);
+                                using var fieldValueVariant = RuntimeTypeConversionHelper.ConvertToVariant(fieldValue);
                                 byte[] valueBuffer = VarToBytes(fieldValueVariant);
                                 writer.Write(valueBuffer.Length);
                                 writer.Write(valueBuffer);
@@ -443,7 +443,14 @@ namespace Godot
 
                             FieldInfo? fieldInfo = targetType.GetField(name,
                                 BindingFlags.Instance | BindingFlags.Public);
-                            fieldInfo?.SetValue(recreatedTarget, GD.BytesToVar(valueBuffer));
+
+                            if (fieldInfo != null)
+                            {
+                                var variantValue = GD.BytesToVar(valueBuffer);
+                                object? managedValue = RuntimeTypeConversionHelper.ConvertToObjectOfType(
+                                    (godot_variant)variantValue.NativeVar, fieldInfo.FieldType);
+                                fieldInfo.SetValue(recreatedTarget, managedValue);
+                            }
                         }
 
                         @delegate = Delegate.CreateDelegate(delegateType, recreatedTarget, methodInfo,
@@ -537,5 +544,269 @@ namespace Godot
 
             return type;
         }
+
+        internal static class RuntimeTypeConversionHelper
+        {
+            [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+            public static godot_variant ConvertToVariant(object? obj)
+            {
+                if (obj == null)
+                    return default;
+
+                switch (obj)
+                {
+                    case bool @bool:
+                        return VariantUtils.CreateFrom(@bool);
+                    case char @char:
+                        return VariantUtils.CreateFrom(@char);
+                    case sbyte int8:
+                        return VariantUtils.CreateFrom(int8);
+                    case short int16:
+                        return VariantUtils.CreateFrom(int16);
+                    case int int32:
+                        return VariantUtils.CreateFrom(int32);
+                    case long int64:
+                        return VariantUtils.CreateFrom(int64);
+                    case byte uint8:
+                        return VariantUtils.CreateFrom(uint8);
+                    case ushort uint16:
+                        return VariantUtils.CreateFrom(uint16);
+                    case uint uint32:
+                        return VariantUtils.CreateFrom(uint32);
+                    case ulong uint64:
+                        return VariantUtils.CreateFrom(uint64);
+                    case float @float:
+                        return VariantUtils.CreateFrom(@float);
+                    case double @double:
+                        return VariantUtils.CreateFrom(@double);
+                    case Vector2 vector2:
+                        return VariantUtils.CreateFrom(vector2);
+                    case Vector2i vector2I:
+                        return VariantUtils.CreateFrom(vector2I);
+                    case Rect2 rect2:
+                        return VariantUtils.CreateFrom(rect2);
+                    case Rect2i rect2I:
+                        return VariantUtils.CreateFrom(rect2I);
+                    case Transform2D transform2D:
+                        return VariantUtils.CreateFrom(transform2D);
+                    case Vector3 vector3:
+                        return VariantUtils.CreateFrom(vector3);
+                    case Vector3i vector3I:
+                        return VariantUtils.CreateFrom(vector3I);
+                    case Vector4 vector4:
+                        return VariantUtils.CreateFrom(vector4);
+                    case Vector4i vector4I:
+                        return VariantUtils.CreateFrom(vector4I);
+                    case Basis basis:
+                        return VariantUtils.CreateFrom(basis);
+                    case Quaternion quaternion:
+                        return VariantUtils.CreateFrom(quaternion);
+                    case Transform3D transform3d:
+                        return VariantUtils.CreateFrom(transform3d);
+                    case Projection projection:
+                        return VariantUtils.CreateFrom(projection);
+                    case AABB aabb:
+                        return VariantUtils.CreateFrom(aabb);
+                    case Color color:
+                        return VariantUtils.CreateFrom(color);
+                    case Plane plane:
+                        return VariantUtils.CreateFrom(plane);
+                    case Callable callable:
+                        return VariantUtils.CreateFrom(callable);
+                    case SignalInfo signalInfo:
+                        return VariantUtils.CreateFrom(signalInfo);
+                    case string @string:
+                        return VariantUtils.CreateFrom(@string);
+                    case byte[] byteArray:
+                        return VariantUtils.CreateFrom(byteArray);
+                    case int[] int32Array:
+                        return VariantUtils.CreateFrom(int32Array);
+                    case long[] int64Array:
+                        return VariantUtils.CreateFrom(int64Array);
+                    case float[] floatArray:
+                        return VariantUtils.CreateFrom(floatArray);
+                    case double[] doubleArray:
+                        return VariantUtils.CreateFrom(doubleArray);
+                    case string[] stringArray:
+                        return VariantUtils.CreateFrom(stringArray);
+                    case Vector2[] vector2Array:
+                        return VariantUtils.CreateFrom(vector2Array);
+                    case Vector3[] vector3Array:
+                        return VariantUtils.CreateFrom(vector3Array);
+                    case Color[] colorArray:
+                        return VariantUtils.CreateFrom(colorArray);
+                    case StringName[] stringNameArray:
+                        return VariantUtils.CreateFrom(stringNameArray);
+                    case NodePath[] nodePathArray:
+                        return VariantUtils.CreateFrom(nodePathArray);
+                    case RID[] ridArray:
+                        return VariantUtils.CreateFrom(ridArray);
+                    case Godot.Object[] godotObjectArray:
+                        return VariantUtils.CreateFrom(godotObjectArray);
+                    case StringName stringName:
+                        return VariantUtils.CreateFrom(stringName);
+                    case NodePath nodePath:
+                        return VariantUtils.CreateFrom(nodePath);
+                    case RID rid:
+                        return VariantUtils.CreateFrom(rid);
+                    case Collections.Dictionary godotDictionary:
+                        return VariantUtils.CreateFrom(godotDictionary);
+                    case Collections.Array godotArray:
+                        return VariantUtils.CreateFrom(godotArray);
+                    case Variant variant:
+                        return VariantUtils.CreateFrom(variant);
+                    case Godot.Object godotObject:
+                        return VariantUtils.CreateFrom(godotObject);
+                    case Enum @enum:
+                        return VariantUtils.CreateFrom(Convert.ToInt64(@enum));
+                    case Collections.IGenericGodotDictionary godotDictionary:
+                        return VariantUtils.CreateFrom(godotDictionary.UnderlyingDictionary);
+                    case Collections.IGenericGodotArray godotArray:
+                        return VariantUtils.CreateFrom(godotArray.UnderlyingArray);
+                }
+
+                GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" +
+                             obj.GetType().FullName + ".");
+                return new godot_variant();
+            }
+
+            private delegate object? ConvertToSystemObjectFunc(in godot_variant managed);
+
+            [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+            // ReSharper disable once RedundantNameQualifier
+            private static readonly System.Collections.Generic.Dictionary<Type, ConvertToSystemObjectFunc>
+                ToSystemObjectFuncByType = new()
+                {
+                    [typeof(bool)] = (in godot_variant variant) => VariantUtils.ConvertTo<bool>(variant),
+                    [typeof(char)] = (in godot_variant variant) => VariantUtils.ConvertTo<char>(variant),
+                    [typeof(sbyte)] = (in godot_variant variant) => VariantUtils.ConvertTo<sbyte>(variant),
+                    [typeof(short)] = (in godot_variant variant) => VariantUtils.ConvertTo<short>(variant),
+                    [typeof(int)] = (in godot_variant variant) => VariantUtils.ConvertTo<int>(variant),
+                    [typeof(long)] = (in godot_variant variant) => VariantUtils.ConvertTo<long>(variant),
+                    [typeof(byte)] = (in godot_variant variant) => VariantUtils.ConvertTo<byte>(variant),
+                    [typeof(ushort)] = (in godot_variant variant) => VariantUtils.ConvertTo<ushort>(variant),
+                    [typeof(uint)] = (in godot_variant variant) => VariantUtils.ConvertTo<uint>(variant),
+                    [typeof(ulong)] = (in godot_variant variant) => VariantUtils.ConvertTo<ulong>(variant),
+                    [typeof(float)] = (in godot_variant variant) => VariantUtils.ConvertTo<float>(variant),
+                    [typeof(double)] = (in godot_variant variant) => VariantUtils.ConvertTo<double>(variant),
+                    [typeof(Vector2)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2>(variant),
+                    [typeof(Vector2i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2i>(variant),
+                    [typeof(Rect2)] = (in godot_variant variant) => VariantUtils.ConvertTo<Rect2>(variant),
+                    [typeof(Rect2i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Rect2i>(variant),
+                    [typeof(Transform2D)] = (in godot_variant variant) => VariantUtils.ConvertTo<Transform2D>(variant),
+                    [typeof(Vector3)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3>(variant),
+                    [typeof(Vector3i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3i>(variant),
+                    [typeof(Basis)] = (in godot_variant variant) => VariantUtils.ConvertTo<Basis>(variant),
+                    [typeof(Quaternion)] = (in godot_variant variant) => VariantUtils.ConvertTo<Quaternion>(variant),
+                    [typeof(Transform3D)] = (in godot_variant variant) => VariantUtils.ConvertTo<Transform3D>(variant),
+                    [typeof(Vector4)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector4>(variant),
+                    [typeof(Vector4i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector4i>(variant),
+                    [typeof(AABB)] = (in godot_variant variant) => VariantUtils.ConvertTo<AABB>(variant),
+                    [typeof(Color)] = (in godot_variant variant) => VariantUtils.ConvertTo<Color>(variant),
+                    [typeof(Plane)] = (in godot_variant variant) => VariantUtils.ConvertTo<Plane>(variant),
+                    [typeof(Callable)] = (in godot_variant variant) => VariantUtils.ConvertTo<Callable>(variant),
+                    [typeof(SignalInfo)] = (in godot_variant variant) => VariantUtils.ConvertTo<SignalInfo>(variant),
+                    [typeof(string)] = (in godot_variant variant) => VariantUtils.ConvertTo<string>(variant),
+                    [typeof(byte[])] = (in godot_variant variant) => VariantUtils.ConvertTo<byte[]>(variant),
+                    [typeof(int[])] = (in godot_variant variant) => VariantUtils.ConvertTo<int[]>(variant),
+                    [typeof(long[])] = (in godot_variant variant) => VariantUtils.ConvertTo<long[]>(variant),
+                    [typeof(float[])] = (in godot_variant variant) => VariantUtils.ConvertTo<float[]>(variant),
+                    [typeof(double[])] = (in godot_variant variant) => VariantUtils.ConvertTo<double[]>(variant),
+                    [typeof(string[])] = (in godot_variant variant) => VariantUtils.ConvertTo<string[]>(variant),
+                    [typeof(Vector2[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2[]>(variant),
+                    [typeof(Vector3[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3[]>(variant),
+                    [typeof(Color[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Color[]>(variant),
+                    [typeof(StringName[])] =
+                        (in godot_variant variant) => VariantUtils.ConvertTo<StringName[]>(variant),
+                    [typeof(NodePath[])] = (in godot_variant variant) => VariantUtils.ConvertTo<NodePath[]>(variant),
+                    [typeof(RID[])] = (in godot_variant variant) => VariantUtils.ConvertTo<RID[]>(variant),
+                    [typeof(StringName)] = (in godot_variant variant) => VariantUtils.ConvertTo<StringName>(variant),
+                    [typeof(NodePath)] = (in godot_variant variant) => VariantUtils.ConvertTo<NodePath>(variant),
+                    [typeof(RID)] = (in godot_variant variant) => VariantUtils.ConvertTo<RID>(variant),
+                    [typeof(Godot.Collections.Dictionary)] = (in godot_variant variant) =>
+                        VariantUtils.ConvertTo<Godot.Collections.Dictionary>(variant),
+                    [typeof(Godot.Collections.Array)] =
+                        (in godot_variant variant) => VariantUtils.ConvertTo<Godot.Collections.Array>(variant),
+                    [typeof(Variant)] = (in godot_variant variant) => VariantUtils.ConvertTo<Variant>(variant),
+                };
+
+            [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+            public static object? ConvertToObjectOfType(in godot_variant variant, Type type)
+            {
+                if (ToSystemObjectFuncByType.TryGetValue(type, out var func))
+                    return func(variant);
+
+                if (typeof(Godot.Object).IsAssignableFrom(type))
+                    return Convert.ChangeType(VariantUtils.ConvertTo<Godot.Object>(variant), type);
+
+                if (type.IsEnum)
+                {
+                    var enumUnderlyingType = type.GetEnumUnderlyingType();
+
+                    switch (Type.GetTypeCode(enumUnderlyingType))
+                    {
+                        case TypeCode.SByte:
+                            return Enum.ToObject(type, VariantUtils.ConvertToInt8(variant));
+                        case TypeCode.Int16:
+                            return Enum.ToObject(type, VariantUtils.ConvertToInt16(variant));
+                        case TypeCode.Int32:
+                            return Enum.ToObject(type, VariantUtils.ConvertToInt32(variant));
+                        case TypeCode.Int64:
+                            return Enum.ToObject(type, VariantUtils.ConvertToInt64(variant));
+                        case TypeCode.Byte:
+                            return Enum.ToObject(type, VariantUtils.ConvertToUInt8(variant));
+                        case TypeCode.UInt16:
+                            return Enum.ToObject(type, VariantUtils.ConvertToUInt16(variant));
+                        case TypeCode.UInt32:
+                            return Enum.ToObject(type, VariantUtils.ConvertToUInt32(variant));
+                        case TypeCode.UInt64:
+                            return Enum.ToObject(type, VariantUtils.ConvertToUInt64(variant));
+                        default:
+                        {
+                            GD.PushError(
+                                "Attempted to convert Variant to enum value of unsupported underlying type. Name: " +
+                                type.FullName + " : " + enumUnderlyingType.FullName + ".");
+                            return null;
+                        }
+                    }
+                }
+
+                if (type.IsGenericType)
+                {
+                    var genericTypeDef = type.GetGenericTypeDefinition();
+
+                    if (genericTypeDef == typeof(Godot.Collections.Dictionary<,>))
+                    {
+                        var ctor = type.GetConstructor(BindingFlags.Default,
+                            new[] { typeof(Godot.Collections.Dictionary) });
+
+                        if (ctor == null)
+                            throw new InvalidOperationException("Dictionary constructor not found");
+
+                        return ctor.Invoke(new object?[]
+                        {
+                            VariantUtils.ConvertTo<Godot.Collections.Dictionary>(variant)
+                        });
+                    }
+
+                    if (genericTypeDef == typeof(Godot.Collections.Array<>))
+                    {
+                        var ctor = type.GetConstructor(BindingFlags.Default,
+                            new[] { typeof(Godot.Collections.Array) });
+
+                        if (ctor == null)
+                            throw new InvalidOperationException("Array constructor not found");
+
+                        return ctor.Invoke(new object?[]
+                        {
+                            VariantUtils.ConvertTo<Godot.Collections.Array>(variant)
+                        });
+                    }
+                }
+
+                GD.PushError($"Attempted to convert Variant to unsupported type. Name: {type.FullName}.");
+                return null;
+            }
+        }
     }
 }

+ 11 - 506
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs

@@ -1,5 +1,7 @@
 using System;
 using System.Runtime.InteropServices;
+using Godot.Collections;
+using Array = System.Array;
 
 // ReSharper disable InconsistentNaming
 
@@ -148,6 +150,15 @@ namespace Godot.NativeInterop
                     {
                         if (typeof(Godot.Object).IsAssignableFrom(type))
                             return Variant.Type.Object;
+
+                        // We use `IsAssignableFrom` with our helper interfaces to detect generic Godot collections
+                        // because `GetGenericTypeDefinition` is not supported in NativeAOT reflection-free mode.
+
+                        if (typeof(IGenericGodotDictionary).IsAssignableFrom(type))
+                            return Variant.Type.Dictionary;
+
+                        if (typeof(IGenericGodotArray).IsAssignableFrom(type))
+                            return Variant.Type.Array;
                     }
                     else if (type == typeof(Variant))
                     {
@@ -183,512 +194,6 @@ namespace Godot.NativeInterop
             return Variant.Type.Nil;
         }
 
-        /* TODO: Reflection and type checking each time is slow. This will be replaced with source generators. */
-        public static godot_variant ConvertManagedObjectToVariant(object? p_obj)
-        {
-            if (p_obj == null)
-                return new godot_variant();
-
-            switch (p_obj)
-            {
-                case bool @bool:
-                    return VariantUtils.CreateFromBool(@bool);
-                case char @char:
-                    return VariantUtils.CreateFromInt(@char);
-                case sbyte @int8:
-                    return VariantUtils.CreateFromInt(@int8);
-                case short @int16:
-                    return VariantUtils.CreateFromInt(@int16);
-                case int @int32:
-                    return VariantUtils.CreateFromInt(@int32);
-                case long @int64:
-                    return VariantUtils.CreateFromInt(@int64);
-                case byte @uint8:
-                    return VariantUtils.CreateFromInt(@uint8);
-                case ushort @uint16:
-                    return VariantUtils.CreateFromInt(@uint16);
-                case uint @uint32:
-                    return VariantUtils.CreateFromInt(@uint32);
-                case ulong @uint64:
-                    return VariantUtils.CreateFromInt(@uint64);
-                case float @float:
-                    return VariantUtils.CreateFromFloat(@float);
-                case double @double:
-                    return VariantUtils.CreateFromFloat(@double);
-                case Vector2 @vector2:
-                    return VariantUtils.CreateFromVector2(@vector2);
-                case Vector2i @vector2i:
-                    return VariantUtils.CreateFromVector2i(@vector2i);
-                case Rect2 @rect2:
-                    return VariantUtils.CreateFromRect2(@rect2);
-                case Rect2i @rect2i:
-                    return VariantUtils.CreateFromRect2i(@rect2i);
-                case Transform2D @transform2D:
-                    return VariantUtils.CreateFromTransform2D(@transform2D);
-                case Vector3 @vector3:
-                    return VariantUtils.CreateFromVector3(@vector3);
-                case Vector3i @vector3i:
-                    return VariantUtils.CreateFromVector3i(@vector3i);
-                case Vector4 @vector4:
-                    return VariantUtils.CreateFromVector4(@vector4);
-                case Vector4i @vector4i:
-                    return VariantUtils.CreateFromVector4i(@vector4i);
-                case Basis @basis:
-                    return VariantUtils.CreateFromBasis(@basis);
-                case Quaternion @quaternion:
-                    return VariantUtils.CreateFromQuaternion(@quaternion);
-                case Transform3D @transform3d:
-                    return VariantUtils.CreateFromTransform3D(@transform3d);
-                case Projection @projection:
-                    return VariantUtils.CreateFromProjection(@projection);
-                case AABB @aabb:
-                    return VariantUtils.CreateFromAABB(@aabb);
-                case Color @color:
-                    return VariantUtils.CreateFromColor(@color);
-                case Plane @plane:
-                    return VariantUtils.CreateFromPlane(@plane);
-                case Callable @callable:
-                    return VariantUtils.CreateFromCallable(@callable);
-                case SignalInfo @signalInfo:
-                    return VariantUtils.CreateFromSignalInfo(@signalInfo);
-                case Enum @enum:
-                    return VariantUtils.CreateFromInt(Convert.ToInt64(@enum));
-                case string @string:
-                    return VariantUtils.CreateFromString(@string);
-                case byte[] byteArray:
-                    return VariantUtils.CreateFromPackedByteArray(byteArray);
-                case int[] int32Array:
-                    return VariantUtils.CreateFromPackedInt32Array(int32Array);
-                case long[] int64Array:
-                    return VariantUtils.CreateFromPackedInt64Array(int64Array);
-                case float[] floatArray:
-                    return VariantUtils.CreateFromPackedFloat32Array(floatArray);
-                case double[] doubleArray:
-                    return VariantUtils.CreateFromPackedFloat64Array(doubleArray);
-                case string[] stringArray:
-                    return VariantUtils.CreateFromPackedStringArray(stringArray);
-                case Vector2[] vector2Array:
-                    return VariantUtils.CreateFromPackedVector2Array(vector2Array);
-                case Vector3[] vector3Array:
-                    return VariantUtils.CreateFromPackedVector3Array(vector3Array);
-                case Color[] colorArray:
-                    return VariantUtils.CreateFromPackedColorArray(colorArray);
-                case StringName[] stringNameArray:
-                    return VariantUtils.CreateFromSystemArrayOfStringName(stringNameArray);
-                case NodePath[] nodePathArray:
-                    return VariantUtils.CreateFromSystemArrayOfNodePath(nodePathArray);
-                case RID[] ridArray:
-                    return VariantUtils.CreateFromSystemArrayOfRID(ridArray);
-                case Godot.Object[] godotObjectArray:
-                    return VariantUtils.CreateFromSystemArrayOfGodotObject(godotObjectArray);
-                case Godot.Object godotObject:
-                    return VariantUtils.CreateFromGodotObject(godotObject);
-                case StringName stringName:
-                    return VariantUtils.CreateFromStringName(stringName);
-                case NodePath nodePath:
-                    return VariantUtils.CreateFromNodePath(nodePath);
-                case RID rid:
-                    return VariantUtils.CreateFromRID(rid);
-                case Collections.Dictionary godotDictionary:
-                    return VariantUtils.CreateFromDictionary(godotDictionary);
-                case Collections.Array godotArray:
-                    return VariantUtils.CreateFromArray(godotArray);
-                case Collections.IGenericGodotDictionary godotDictionary:
-                    return VariantUtils.CreateFromDictionary(godotDictionary.UnderlyingDictionary);
-                case Collections.IGenericGodotArray godotArray:
-                    return VariantUtils.CreateFromArray(godotArray.UnderlyingArray);
-                case Variant variant:
-                    return NativeFuncs.godotsharp_variant_new_copy((godot_variant)variant.NativeVar);
-            }
-
-            GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" +
-                         p_obj.GetType().FullName + ".");
-            return new godot_variant();
-        }
-
-        public static object? ConvertVariantToManagedObjectOfType(in godot_variant p_var, Type type)
-        {
-            // This function is only needed to set the value of properties. Fields have their own implementation, set_value_from_variant.
-            switch (Type.GetTypeCode(type))
-            {
-                case TypeCode.Boolean:
-                    return VariantUtils.ConvertToBool(p_var);
-                case TypeCode.Char:
-                    return VariantUtils.ConvertToChar(p_var);
-                case TypeCode.SByte:
-                    return VariantUtils.ConvertToInt8(p_var);
-                case TypeCode.Int16:
-                    return VariantUtils.ConvertToInt16(p_var);
-                case TypeCode.Int32:
-                    return VariantUtils.ConvertToInt32(p_var);
-                case TypeCode.Int64:
-                    return VariantUtils.ConvertToInt64(p_var);
-                case TypeCode.Byte:
-                    return VariantUtils.ConvertToUInt8(p_var);
-                case TypeCode.UInt16:
-                    return VariantUtils.ConvertToUInt16(p_var);
-                case TypeCode.UInt32:
-                    return VariantUtils.ConvertToUInt32(p_var);
-                case TypeCode.UInt64:
-                    return VariantUtils.ConvertToUInt64(p_var);
-                case TypeCode.Single:
-                    return VariantUtils.ConvertToFloat32(p_var);
-                case TypeCode.Double:
-                    return VariantUtils.ConvertToFloat64(p_var);
-                case TypeCode.String:
-                    return VariantUtils.ConvertToStringObject(p_var);
-                default:
-                {
-                    if (type == typeof(Vector2))
-                        return VariantUtils.ConvertToVector2(p_var);
-
-                    if (type == typeof(Vector2i))
-                        return VariantUtils.ConvertToVector2i(p_var);
-
-                    if (type == typeof(Rect2))
-                        return VariantUtils.ConvertToRect2(p_var);
-
-                    if (type == typeof(Rect2i))
-                        return VariantUtils.ConvertToRect2i(p_var);
-
-                    if (type == typeof(Transform2D))
-                        return VariantUtils.ConvertToTransform2D(p_var);
-
-                    if (type == typeof(Vector3))
-                        return VariantUtils.ConvertToVector3(p_var);
-
-                    if (type == typeof(Vector3i))
-                        return VariantUtils.ConvertToVector3i(p_var);
-
-                    if (type == typeof(Vector4))
-                        return VariantUtils.ConvertToVector4(p_var);
-
-                    if (type == typeof(Vector4i))
-                        return VariantUtils.ConvertToVector4i(p_var);
-
-                    if (type == typeof(Basis))
-                        return VariantUtils.ConvertToBasis(p_var);
-
-                    if (type == typeof(Quaternion))
-                        return VariantUtils.ConvertToQuaternion(p_var);
-
-                    if (type == typeof(Transform3D))
-                        return VariantUtils.ConvertToTransform3D(p_var);
-
-                    if (type == typeof(Projection))
-                        return VariantUtils.ConvertToProjection(p_var);
-
-                    if (type == typeof(AABB))
-                        return VariantUtils.ConvertToAABB(p_var);
-
-                    if (type == typeof(Color))
-                        return VariantUtils.ConvertToColor(p_var);
-
-                    if (type == typeof(Plane))
-                        return VariantUtils.ConvertToPlane(p_var);
-
-                    if (type == typeof(Callable))
-                        return VariantUtils.ConvertToCallableManaged(p_var);
-
-                    if (type == typeof(SignalInfo))
-                        return VariantUtils.ConvertToSignalInfo(p_var);
-
-                    if (type.IsEnum)
-                    {
-                        var enumUnderlyingType = type.GetEnumUnderlyingType();
-                        switch (Type.GetTypeCode(enumUnderlyingType))
-                        {
-                            case TypeCode.SByte:
-                                return VariantUtils.ConvertToInt8(p_var);
-                            case TypeCode.Int16:
-                                return VariantUtils.ConvertToInt16(p_var);
-                            case TypeCode.Int32:
-                                return VariantUtils.ConvertToInt32(p_var);
-                            case TypeCode.Int64:
-                                return VariantUtils.ConvertToInt64(p_var);
-                            case TypeCode.Byte:
-                                return VariantUtils.ConvertToUInt8(p_var);
-                            case TypeCode.UInt16:
-                                return VariantUtils.ConvertToUInt16(p_var);
-                            case TypeCode.UInt32:
-                                return VariantUtils.ConvertToUInt32(p_var);
-                            case TypeCode.UInt64:
-                                return VariantUtils.ConvertToUInt64(p_var);
-                            default:
-                            {
-                                GD.PushError(
-                                    "Attempted to convert Variant to enum value of unsupported underlying type. Name: " +
-                                    type.FullName + " : " + enumUnderlyingType.FullName + ".");
-                                return null;
-                            }
-                        }
-                    }
-
-                    if (type.IsArray || type.IsSZArray)
-                    {
-                        return ConvertVariantToSystemArrayOfType(p_var, type);
-                    }
-                    else if (type.IsGenericType)
-                    {
-                        if (typeof(Godot.Object).IsAssignableFrom(type))
-                        {
-                            var godotObject = VariantUtils.ConvertToGodotObject(p_var);
-
-                            if (!type.IsInstanceOfType(godotObject))
-                            {
-                                GD.PushError("Invalid cast when marshaling Godot.Object type." +
-                                             $" `{godotObject.GetType()}` is not assignable to `{type.FullName}`.");
-                                return null;
-                            }
-
-                            return godotObject;
-                        }
-
-                        return null;
-                    }
-                    else if (type == typeof(Variant))
-                    {
-                        return Variant.CreateCopyingBorrowed(p_var);
-                    }
-
-                    if (ConvertVariantToManagedObjectOfClass(p_var, type, out object? res))
-                        return res;
-
-                    break;
-                }
-            }
-
-            GD.PushError("Attempted to convert Variant to unsupported type. Name: " +
-                         type.FullName + ".");
-            return null;
-        }
-
-        private static object? ConvertVariantToSystemArrayOfType(in godot_variant p_var, Type type)
-        {
-            if (type == typeof(byte[]))
-                return VariantUtils.ConvertAsPackedByteArrayToSystemArray(p_var);
-
-            if (type == typeof(int[]))
-                return VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(p_var);
-
-            if (type == typeof(long[]))
-                return VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(p_var);
-
-            if (type == typeof(float[]))
-                return VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(p_var);
-
-            if (type == typeof(double[]))
-                return VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(p_var);
-
-            if (type == typeof(string[]))
-                return VariantUtils.ConvertAsPackedStringArrayToSystemArray(p_var);
-
-            if (type == typeof(Vector2[]))
-                return VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(p_var);
-
-            if (type == typeof(Vector3[]))
-                return VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(p_var);
-
-            if (type == typeof(Color[]))
-                return VariantUtils.ConvertAsPackedColorArrayToSystemArray(p_var);
-
-            if (type == typeof(StringName[]))
-                return VariantUtils.ConvertToSystemArrayOfStringName(p_var);
-
-            if (type == typeof(NodePath[]))
-                return VariantUtils.ConvertToSystemArrayOfNodePath(p_var);
-
-            if (type == typeof(RID[]))
-                return VariantUtils.ConvertToSystemArrayOfRID(p_var);
-
-            if (typeof(Godot.Object[]).IsAssignableFrom(type))
-                return VariantUtils.ConvertToSystemArrayOfGodotObject(p_var, type);
-
-            GD.PushError("Attempted to convert Variant to array of unsupported element type. Name: " +
-                         type.GetElementType()!.FullName + ".");
-            return null;
-        }
-
-        private static bool ConvertVariantToManagedObjectOfClass(in godot_variant p_var, Type type,
-            out object? res)
-        {
-            if (typeof(Godot.Object).IsAssignableFrom(type))
-            {
-                if (p_var.Type == Variant.Type.Nil)
-                {
-                    res = null;
-                    return true;
-                }
-
-                if (p_var.Type != Variant.Type.Object)
-                {
-                    GD.PushError("Invalid cast when marshaling Godot.Object type." +
-                                 $" Variant type is `{p_var.Type}`; expected `{p_var.Object}`.");
-                    res = null;
-                    return true;
-                }
-
-                var godotObjectPtr = VariantUtils.ConvertToGodotObjectPtr(p_var);
-
-                if (godotObjectPtr == IntPtr.Zero)
-                {
-                    res = null;
-                    return true;
-                }
-
-                var godotObject = InteropUtils.UnmanagedGetManaged(godotObjectPtr);
-
-                if (!type.IsInstanceOfType(godotObject))
-                {
-                    GD.PushError("Invalid cast when marshaling Godot.Object type." +
-                                 $" `{godotObject.GetType()}` is not assignable to `{type.FullName}`.");
-                    res = null;
-                    return false;
-                }
-
-                res = godotObject;
-                return true;
-            }
-
-            if (typeof(StringName) == type)
-            {
-                res = VariantUtils.ConvertToStringNameObject(p_var);
-                return true;
-            }
-
-            if (typeof(NodePath) == type)
-            {
-                res = VariantUtils.ConvertToNodePathObject(p_var);
-                return true;
-            }
-
-            if (typeof(RID) == type)
-            {
-                res = VariantUtils.ConvertToRID(p_var);
-                return true;
-            }
-
-            if (typeof(Collections.Dictionary) == type)
-            {
-                res = VariantUtils.ConvertToDictionaryObject(p_var);
-                return true;
-            }
-
-            if (typeof(Collections.Array) == type)
-            {
-                res = VariantUtils.ConvertToArrayObject(p_var);
-                return true;
-            }
-
-            res = null;
-            return false;
-        }
-
-        public static unsafe object? ConvertVariantToManagedObject(in godot_variant p_var)
-        {
-            switch (p_var.Type)
-            {
-                case Variant.Type.Bool:
-                    return p_var.Bool.ToBool();
-                case Variant.Type.Int:
-                    return p_var.Int;
-                case Variant.Type.Float:
-                {
-#if REAL_T_IS_DOUBLE
-                    return p_var.Float;
-#else
-                    return (float)p_var.Float;
-#endif
-                }
-                case Variant.Type.String:
-                    return ConvertStringToManaged(p_var.String);
-                case Variant.Type.Vector2:
-                    return p_var.Vector2;
-                case Variant.Type.Vector2i:
-                    return p_var.Vector2i;
-                case Variant.Type.Rect2:
-                    return p_var.Rect2;
-                case Variant.Type.Rect2i:
-                    return p_var.Rect2i;
-                case Variant.Type.Vector3:
-                    return p_var.Vector3;
-                case Variant.Type.Vector3i:
-                    return p_var.Vector3i;
-                case Variant.Type.Transform2d:
-                    return *p_var.Transform2D;
-                case Variant.Type.Vector4:
-                    return p_var.Vector4;
-                case Variant.Type.Vector4i:
-                    return p_var.Vector4i;
-                case Variant.Type.Plane:
-                    return p_var.Plane;
-                case Variant.Type.Quaternion:
-                    return p_var.Quaternion;
-                case Variant.Type.Aabb:
-                    return *p_var.AABB;
-                case Variant.Type.Basis:
-                    return *p_var.Basis;
-                case Variant.Type.Transform3d:
-                    return *p_var.Transform3D;
-                case Variant.Type.Projection:
-                    return *p_var.Projection;
-                case Variant.Type.Color:
-                    return p_var.Color;
-                case Variant.Type.StringName:
-                {
-                    // The Variant owns the value, so we need to make a copy
-                    return StringName.CreateTakingOwnershipOfDisposableValue(
-                        NativeFuncs.godotsharp_string_name_new_copy(p_var.StringName));
-                }
-                case Variant.Type.NodePath:
-                {
-                    // The Variant owns the value, so we need to make a copy
-                    return NodePath.CreateTakingOwnershipOfDisposableValue(
-                        NativeFuncs.godotsharp_node_path_new_copy(p_var.NodePath));
-                }
-                case Variant.Type.Rid:
-                    return p_var.RID;
-                case Variant.Type.Object:
-                    return InteropUtils.UnmanagedGetManaged(p_var.Object);
-                case Variant.Type.Callable:
-                    return ConvertCallableToManaged(p_var.Callable);
-                case Variant.Type.Signal:
-                    return ConvertSignalToManaged(p_var.Signal);
-                case Variant.Type.Dictionary:
-                {
-                    // The Variant owns the value, so we need to make a copy
-                    return Collections.Dictionary.CreateTakingOwnershipOfDisposableValue(
-                        NativeFuncs.godotsharp_dictionary_new_copy(p_var.Dictionary));
-                }
-                case Variant.Type.Array:
-                {
-                    // The Variant owns the value, so we need to make a copy
-                    return Collections.Array.CreateTakingOwnershipOfDisposableValue(
-                        NativeFuncs.godotsharp_array_new_copy(p_var.Array));
-                }
-                case Variant.Type.PackedByteArray:
-                    return VariantUtils.ConvertAsPackedByteArrayToSystemArray(p_var);
-                case Variant.Type.PackedInt32Array:
-                    return VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(p_var);
-                case Variant.Type.PackedInt64Array:
-                    return VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(p_var);
-                case Variant.Type.PackedFloat32Array:
-                    return VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(p_var);
-                case Variant.Type.PackedFloat64Array:
-                    return VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(p_var);
-                case Variant.Type.PackedStringArray:
-                    return VariantUtils.ConvertAsPackedStringArrayToSystemArray(p_var);
-                case Variant.Type.PackedVector2Array:
-                    return VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(p_var);
-                case Variant.Type.PackedVector3Array:
-                    return VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(p_var);
-                case Variant.Type.PackedColorArray:
-                    return VariantUtils.ConvertAsPackedColorArrayToSystemArray(p_var);
-                default:
-                    return null;
-            }
-        }
-
         // String
 
         public static unsafe godot_string ConvertStringToNative(string? p_mono_string)

+ 2 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs

@@ -4,6 +4,8 @@ using System.Runtime.CompilerServices;
 
 namespace Godot.NativeInterop;
 
+#nullable enable
+
 public partial class VariantUtils
 {
     private static Exception UnsupportedType<T>() => new InvalidOperationException(

+ 47 - 9
modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs

@@ -109,16 +109,54 @@ public partial struct Variant : IDisposable
 
     public override string ToString() => AsString();
 
-    public object? Obj
-    {
-        get
+    public object? Obj =>
+        _obj ??= NativeVar.DangerousSelfRef.Type switch
         {
-            if (_obj == null)
-                _obj = Marshaling.ConvertVariantToManagedObject((godot_variant)NativeVar);
-
-            return _obj;
-        }
-    }
+            Type.Bool => AsBool(),
+            Type.Int => AsInt64(),
+#if REAL_T_IS_DOUBLE
+            Type.Float => AsDouble(),
+#else
+            Type.Float => AsSingle(),
+#endif
+            Type.String => AsString(),
+            Type.Vector2 => AsVector2(),
+            Type.Vector2i => AsVector2i(),
+            Type.Rect2 => AsRect2(),
+            Type.Rect2i => AsRect2i(),
+            Type.Vector3 => AsVector3(),
+            Type.Vector3i => AsVector3i(),
+            Type.Transform2d => AsTransform2D(),
+            Type.Vector4 => AsVector4(),
+            Type.Vector4i => AsVector4i(),
+            Type.Plane => AsPlane(),
+            Type.Quaternion => AsQuaternion(),
+            Type.Aabb => AsAABB(),
+            Type.Basis => AsBasis(),
+            Type.Transform3d => AsTransform3D(),
+            Type.Projection => AsProjection(),
+            Type.Color => AsColor(),
+            Type.StringName => AsStringName(),
+            Type.NodePath => AsNodePath(),
+            Type.Rid => AsRID(),
+            Type.Object => AsGodotObject(),
+            Type.Callable => AsCallable(),
+            Type.Signal => AsSignalInfo(),
+            Type.Dictionary => AsGodotDictionary(),
+            Type.Array => AsGodotArray(),
+            Type.PackedByteArray => AsByteArray(),
+            Type.PackedInt32Array => AsInt32Array(),
+            Type.PackedInt64Array => AsInt64Array(),
+            Type.PackedFloat32Array => AsFloat32Array(),
+            Type.PackedFloat64Array => AsFloat64Array(),
+            Type.PackedStringArray => AsStringArray(),
+            Type.PackedVector2Array => AsVector2Array(),
+            Type.PackedVector3Array => AsVector3Array(),
+            Type.PackedColorArray => AsColorArray(),
+            Type.Nil => null,
+            Type.Max or _ =>
+                throw new InvalidOperationException($"Invalid Variant type: {NativeVar.DangerousSelfRef.Type}"),
+        };
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static Variant From<[MustBeVariant] T>(in T from) =>