ソースを参照

Merge pull request #82983 from Repiteo/c#-godotsharp-nullable-classes

[C#] Enable nullability for variant classes
Yuri Sizov 1 年間 前
コミット
ef79e5d9dd

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

@@ -6,6 +6,8 @@ using System.Linq;
 using System.Runtime.CompilerServices;
 using Godot.NativeInterop;
 
+#nullable enable
+
 namespace Godot.Collections
 {
     /// <summary>
@@ -22,7 +24,7 @@ namespace Godot.Collections
     {
         internal godot_array.movable NativeValue;
 
-        private WeakReference<IDisposable> _weakReferenceToSelf;
+        private WeakReference<IDisposable>? _weakReferenceToSelf;
 
         /// <summary>
         /// Constructs a new empty <see cref="Array"/>.
@@ -1140,7 +1142,8 @@ namespace Godot.Collections
         /// </summary>
         /// <param name="from">The typed array to convert.</param>
         /// <returns>A new Godot Array, or <see langword="null"/> if <see paramref="from"/> was null.</returns>
-        public static explicit operator Array(Array<T> from)
+        [return: NotNullIfNotNull(nameof(from))]
+        public static explicit operator Array?(Array<T>? from)
         {
             return from?._underlyingArray;
         }

+ 8 - 4
modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs

@@ -194,7 +194,7 @@ namespace Godot.Bridge
 
                 var native = GodotObject.InternalGetClassNativeBase(scriptType);
 
-                var field = native?.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static |
+                var field = native.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static |
                                                            BindingFlags.Public | BindingFlags.NonPublic);
 
                 if (field == null)
@@ -253,11 +253,15 @@ namespace Godot.Bridge
             {
                 var editorAssembly = AppDomain.CurrentDomain.GetAssemblies()
                     .FirstOrDefault(a => a.GetName().Name == "GodotSharpEditor");
-                wrapperType = editorAssembly?.GetType("Godot." + nativeTypeNameStr);
 
-                if (wrapperType == null)
+                if (editorAssembly != null)
                 {
-                    wrapperType = GetTypeByGodotClassAttr(editorAssembly, nativeTypeNameStr);
+                    wrapperType = editorAssembly.GetType("Godot." + nativeTypeNameStr);
+
+                    if (wrapperType == null)
+                    {
+                        wrapperType = GetTypeByGodotClassAttr(editorAssembly, nativeTypeNameStr);
+                    }
                 }
             }
 

+ 1 - 1
modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs

@@ -414,7 +414,7 @@ namespace Godot
                     {
                         ulong objectId = reader.ReadUInt64();
                         // ReSharper disable once RedundantNameQualifier
-                        GodotObject godotObject = GodotObject.InstanceFromId(objectId);
+                        GodotObject? godotObject = GodotObject.InstanceFromId(objectId);
                         if (godotObject == null)
                             return false;
 

+ 5 - 2
modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs

@@ -5,6 +5,8 @@ using System.Diagnostics.CodeAnalysis;
 using System.Runtime.CompilerServices;
 using Godot.NativeInterop;
 
+#nullable enable
+
 namespace Godot.Collections
 {
     /// <summary>
@@ -19,7 +21,7 @@ namespace Godot.Collections
     {
         internal godot_dictionary.movable NativeValue;
 
-        private WeakReference<IDisposable> _weakReferenceToSelf;
+        private WeakReference<IDisposable>? _weakReferenceToSelf;
 
         /// <summary>
         /// Constructs a new empty <see cref="Dictionary"/>.
@@ -559,7 +561,8 @@ namespace Godot.Collections
         /// </summary>
         /// <param name="from">The typed dictionary to convert.</param>
         /// <returns>A new Godot Dictionary, or <see langword="null"/> if <see paramref="from"/> was null.</returns>
-        public static explicit operator Dictionary(Dictionary<TKey, TValue> from)
+        [return: NotNullIfNotNull(nameof(from))]
+        public static explicit operator Dictionary?(Dictionary<TKey, TValue>? from)
         {
             return from?._underlyingDict;
         }

+ 6 - 4
modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs

@@ -1,6 +1,8 @@
 using System;
 using Godot.NativeInterop;
 
+#nullable enable
+
 namespace Godot
 {
     public partial class GodotObject
@@ -26,7 +28,7 @@ namespace Godot
         /// </example>
         /// <param name="instanceId">Instance ID of the Object to retrieve.</param>
         /// <returns>The <see cref="GodotObject"/> instance.</returns>
-        public static GodotObject InstanceFromId(ulong instanceId)
+        public static GodotObject? InstanceFromId(ulong instanceId)
         {
             return InteropUtils.UnmanagedGetManaged(NativeFuncs.godotsharp_instance_from_id(instanceId));
         }
@@ -49,7 +51,7 @@ namespace Godot
         /// </summary>
         /// <param name="instance">The instance to check.</param>
         /// <returns>If the instance is a valid object.</returns>
-        public static bool IsInstanceValid(GodotObject instance)
+        public static bool IsInstanceValid(GodotObject? instance)
         {
             return instance != null && instance.NativeInstance != IntPtr.Zero;
         }
@@ -66,9 +68,9 @@ namespace Godot
         /// </summary>
         /// <param name="obj">The object.</param>
         /// <returns>
-        /// The <see cref="WeakRef"/> reference to the object or <see langword="null"/>.
+        /// The <see cref="Godot.WeakRef"/> reference to the object or <see langword="null"/>.
         /// </returns>
-        public static WeakRef WeakRef(GodotObject obj)
+        public static WeakRef? WeakRef(GodotObject? obj)
         {
             if (!IsInstanceValid(obj))
                 return null;

+ 11 - 12
modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs

@@ -1,8 +1,11 @@
 using System;
+using System.Diagnostics;
 using System.Runtime.InteropServices;
 using Godot.Bridge;
 using Godot.NativeInterop;
 
+#nullable enable
+
 namespace Godot
 {
     public partial class GodotObject : IDisposable
@@ -13,7 +16,7 @@ namespace Godot
         internal IntPtr NativePtr;
         private bool _memoryOwn;
 
-        private WeakReference<GodotObject> _weakReferenceToSelf;
+        private WeakReference<GodotObject>? _weakReferenceToSelf;
 
         /// <summary>
         /// Constructs a new <see cref="GodotObject"/>.
@@ -59,7 +62,7 @@ namespace Godot
         /// </summary>
         public IntPtr NativeInstance => NativePtr;
 
-        internal static IntPtr GetPtr(GodotObject instance)
+        internal static IntPtr GetPtr(GodotObject? instance)
         {
             if (instance == null)
                 return IntPtr.Zero;
@@ -105,7 +108,7 @@ namespace Godot
 
                 if (gcHandleToFree != IntPtr.Zero)
                 {
-                    object target = GCHandle.FromIntPtr(gcHandleToFree).Target;
+                    object? target = GCHandle.FromIntPtr(gcHandleToFree).Target;
                     // The GC handle may have been replaced in another thread. Release it only if
                     // it's associated to this managed instance, or if the target is no longer alive.
                     if (target != this && target != null)
@@ -176,18 +179,14 @@ namespace Godot
 
         internal static Type InternalGetClassNativeBase(Type t)
         {
-            do
-            {
-                var assemblyName = t.Assembly.GetName();
+            var name = t.Assembly.GetName().Name;
 
-                if (assemblyName.Name == "GodotSharp")
-                    return t;
+            if (name == "GodotSharp" || name == "GodotSharpEditor")
+                return t;
 
-                if (assemblyName.Name == "GodotSharpEditor")
-                    return t;
-            } while ((t = t.BaseType) != null);
+            Debug.Assert(t.BaseType is not null, "Script types must derive from a native Godot type.");
 
-            return null;
+            return InternalGetClassNativeBase(t.BaseType);
         }
 
         // ReSharper disable once VirtualMemberNeverOverridden.Global

+ 11 - 7
modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs

@@ -1,6 +1,9 @@
 using System;
+using System.Diagnostics.CodeAnalysis;
 using Godot.NativeInterop;
 
+#nullable enable
+
 namespace Godot
 {
     /// <summary>
@@ -39,11 +42,11 @@ namespace Godot
     /// new NodePath("/root/MyAutoload"); // If you have an autoloaded node or scene.
     /// </code>
     /// </example>
-    public sealed class NodePath : IDisposable, IEquatable<NodePath>
+    public sealed class NodePath : IDisposable, IEquatable<NodePath?>
     {
         internal godot_node_path.movable NativeValue;
 
-        private WeakReference<IDisposable> _weakReferenceToSelf;
+        private WeakReference<IDisposable>? _weakReferenceToSelf;
 
         ~NodePath()
         {
@@ -135,7 +138,8 @@ namespace Godot
         /// Converts this <see cref="NodePath"/> to a string.
         /// </summary>
         /// <param name="from">The <see cref="NodePath"/> to convert.</param>
-        public static implicit operator string(NodePath from) => from?.ToString();
+        [return: NotNullIfNotNull(nameof(from))]
+        public static implicit operator string?(NodePath? from) => from?.ToString();
 
         /// <summary>
         /// Converts this <see cref="NodePath"/> to a string.
@@ -289,19 +293,19 @@ namespace Godot
         /// <returns>If the <see cref="NodePath"/> is empty.</returns>
         public bool IsEmpty => NativeValue.DangerousSelfRef.IsEmpty;
 
-        public static bool operator ==(NodePath left, NodePath right)
+        public static bool operator ==(NodePath? left, NodePath? right)
         {
             if (left is null)
                 return right is null;
             return left.Equals(right);
         }
 
-        public static bool operator !=(NodePath left, NodePath right)
+        public static bool operator !=(NodePath? left, NodePath? right)
         {
             return !(left == right);
         }
 
-        public bool Equals(NodePath other)
+        public bool Equals([NotNullWhen(true)] NodePath? other)
         {
             if (other is null)
                 return false;
@@ -310,7 +314,7 @@ namespace Godot
             return NativeFuncs.godotsharp_node_path_equals(self, otherNative).ToBool();
         }
 
-        public override bool Equals(object obj)
+        public override bool Equals([NotNullWhen(true)] object? obj)
         {
             return ReferenceEquals(this, obj) || (obj is NodePath other && Equals(other));
         }

+ 15 - 11
modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs

@@ -1,6 +1,9 @@
 using System;
+using System.Diagnostics.CodeAnalysis;
 using Godot.NativeInterop;
 
+#nullable enable
+
 namespace Godot
 {
     /// <summary>
@@ -10,11 +13,11 @@ namespace Godot
     /// Comparing them is much faster than with regular strings, because only the pointers are compared,
     /// not the whole strings.
     /// </summary>
-    public sealed class StringName : IDisposable, IEquatable<StringName>
+    public sealed class StringName : IDisposable, IEquatable<StringName?>
     {
         internal godot_string_name.movable NativeValue;
 
-        private WeakReference<IDisposable> _weakReferenceToSelf;
+        private WeakReference<IDisposable>? _weakReferenceToSelf;
 
         ~StringName()
         {
@@ -81,7 +84,8 @@ namespace Godot
         /// Converts a <see cref="StringName"/> to a string.
         /// </summary>
         /// <param name="from">The <see cref="StringName"/> to convert.</param>
-        public static implicit operator string(StringName from) => from?.ToString();
+        [return: NotNullIfNotNull(nameof(from))]
+        public static implicit operator string?(StringName? from) => from?.ToString();
 
         /// <summary>
         /// Converts this <see cref="StringName"/> to a string.
@@ -104,43 +108,43 @@ namespace Godot
         /// <returns>If the <see cref="StringName"/> is empty.</returns>
         public bool IsEmpty => NativeValue.DangerousSelfRef.IsEmpty;
 
-        public static bool operator ==(StringName left, StringName right)
+        public static bool operator ==(StringName? left, StringName? right)
         {
             if (left is null)
                 return right is null;
             return left.Equals(right);
         }
 
-        public static bool operator !=(StringName left, StringName right)
+        public static bool operator !=(StringName? left, StringName? right)
         {
             return !(left == right);
         }
 
-        public bool Equals(StringName other)
+        public bool Equals([NotNullWhen(true)] StringName? other)
         {
             if (other is null)
                 return false;
             return NativeValue.DangerousSelfRef == other.NativeValue.DangerousSelfRef;
         }
 
-        public static bool operator ==(StringName left, in godot_string_name right)
+        public static bool operator ==(StringName? left, in godot_string_name right)
         {
             if (left is null)
                 return right.IsEmpty;
             return left.Equals(right);
         }
 
-        public static bool operator !=(StringName left, in godot_string_name right)
+        public static bool operator !=(StringName? left, in godot_string_name right)
         {
             return !(left == right);
         }
 
-        public static bool operator ==(in godot_string_name left, StringName right)
+        public static bool operator ==(in godot_string_name left, StringName? right)
         {
             return right == left;
         }
 
-        public static bool operator !=(in godot_string_name left, StringName right)
+        public static bool operator !=(in godot_string_name left, StringName? right)
         {
             return !(right == left);
         }
@@ -150,7 +154,7 @@ namespace Godot
             return NativeValue.DangerousSelfRef == other;
         }
 
-        public override bool Equals(object obj)
+        public override bool Equals([NotNullWhen(true)] object? obj)
         {
             return ReferenceEquals(this, obj) || (obj is StringName other && Equals(other));
         }