From f033764ffe5892f963a9416e8cbcfd0fb5225103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sat, 28 May 2022 04:56:54 +0200 Subject: C#: Refactor Array & Dictionary interface implementations --- .../mono/glue/GodotSharp/GodotSharp/Core/Array.cs | 93 ++++++--- .../glue/GodotSharp/GodotSharp/Core/Dictionary.cs | 208 +++++++++++++++------ .../GodotSharp/Core/NativeInterop/Marshaling.cs | 2 +- .../GodotSharp/Core/NativeInterop/VariantUtils.cs | 4 +- 4 files changed, 217 insertions(+), 90 deletions(-) (limited to 'modules/mono/glue') diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index a09ba09e95..3a1a8ac563 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -14,7 +14,9 @@ namespace Godot.Collections /// such as or . /// public sealed class Array : - IList, + IList, + IReadOnlyList, + ICollection, IDisposable { internal godot_array.movable NativeValue; @@ -163,12 +165,6 @@ namespace Godot.Collections return newArray; } - // IList - - bool IList.IsReadOnly => false; - - bool IList.IsFixedSize => false; - /// /// Returns the object at the given . /// @@ -194,21 +190,21 @@ namespace Godot.Collections /// Adds an object to the end of this . /// This is the same as append or push_back in GDScript. /// - /// The object to add. + /// The object to add. /// The new size after adding the object. - public int Add(object value) + public void Add(object item) { - using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(value); + using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item); var self = (godot_array)NativeValue; - return NativeFuncs.godotsharp_array_add(ref self, variantValue); + _ = NativeFuncs.godotsharp_array_add(ref self, variantValue); } /// /// Checks if this contains the given object. /// - /// The item to look for. + /// The item to look for. /// Whether or not this array contains the given object. - public bool Contains(object value) => IndexOf(value) != -1; + public bool Contains(object item) => IndexOf(item) != -1; /// /// Erases all items from this . @@ -219,11 +215,11 @@ namespace Godot.Collections /// Searches this for an object /// and returns its index or -1 if not found. /// - /// The object to search for. + /// The object to search for. /// The index of the object, or -1 if not found. - public int IndexOf(object value) + public int IndexOf(object item) { - using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(value); + using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item); var self = (godot_array)NativeValue; return NativeFuncs.godotsharp_array_index_of(ref self, variantValue); } @@ -235,27 +231,32 @@ namespace Godot.Collections /// Existing items will be moved to the right. /// /// The index to insert at. - /// The object to insert. - public void Insert(int index, object value) + /// The object to insert. + public void Insert(int index, object item) { if (index < 0 || index > Count) throw new ArgumentOutOfRangeException(nameof(index)); - using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(value); + using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item); var self = (godot_array)NativeValue; NativeFuncs.godotsharp_array_insert(ref self, index, variantValue); } /// - /// Removes the first occurrence of the specified + /// Removes the first occurrence of the specified /// from this . /// - /// The value to remove. - public void Remove(object value) + /// The value to remove. + public bool Remove(object item) { - int index = IndexOf(value); + int index = IndexOf(item); if (index >= 0) + { RemoveAt(index); + return true; + } + + return false; } /// @@ -280,17 +281,49 @@ namespace Godot.Collections /// The number of elements. public int Count => NativeValue.DangerousSelfRef.Size; - object ICollection.SyncRoot => this; + public bool IsSynchronized => false; - bool ICollection.IsSynchronized => false; + public object SyncRoot => false; + + public bool IsReadOnly => false; /// /// Copies the elements of this to the given /// untyped C# array, starting at the given index. /// /// The array to copy to. - /// The index to start at. - public void CopyTo(System.Array array, int index) + /// The index to start at. + public void CopyTo(object[] array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException(nameof(array), "Value cannot be null."); + + if (arrayIndex < 0) + { + throw new ArgumentOutOfRangeException(nameof(arrayIndex), + "Number was less than the array's lower bound in the first dimension."); + } + + int count = Count; + + if (array.Length < (arrayIndex + count)) + { + throw new ArgumentException( + "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + } + + unsafe + { + for (int i = 0; i < count; i++) + { + object obj = Marshaling.ConvertVariantToManagedObject(NativeValue.DangerousSelfRef.Elements[i]); + array[arrayIndex] = obj; + arrayIndex++; + } + } + } + + void ICollection.CopyTo(System.Array array, int index) { if (array == null) throw new ArgumentNullException(nameof(array), "Value cannot be null."); @@ -357,7 +390,7 @@ namespace Godot.Collections /// Gets an enumerator for this . /// /// An enumerator. - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { int count = Count; @@ -367,6 +400,8 @@ namespace Godot.Collections } } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + /// /// Converts this to a string. /// @@ -632,7 +667,7 @@ namespace Godot.Collections /// The C# array to copy to. /// The index to start at. public void CopyTo(T[] array, int arrayIndex) => - _underlyingArray.CopyToGeneric(array, arrayIndex, TypeOfElements); + _underlyingArray.CopyToGeneric(array, arrayIndex, TypeOfElements); /// /// Removes the first occurrence of the specified value diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index 2523728c8b..13aae72660 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -4,6 +4,7 @@ using System.Collections; using Godot.NativeInterop; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using System.Linq; namespace Godot.Collections { @@ -13,7 +14,9 @@ namespace Godot.Collections /// interfacing with the engine. /// public sealed class Dictionary : + IDictionary, IDictionary, + IReadOnlyDictionary, IDisposable { internal godot_dictionary.movable NativeValue; @@ -94,7 +97,7 @@ namespace Godot.Collections /// /// Gets the collection of keys in this . /// - public ICollection Keys + public ICollection Keys { get { @@ -108,7 +111,7 @@ namespace Godot.Collections /// /// Gets the collection of elements in this . /// - public ICollection Values + public ICollection Values { get { @@ -119,6 +122,14 @@ namespace Godot.Collections } } + IEnumerable IReadOnlyDictionary.Keys => Keys; + + IEnumerable IReadOnlyDictionary.Values => Values; + + ICollection IDictionary.Keys => Keys.ToList(); + + ICollection IDictionary.Values => Values.ToList(); + private (Array keys, Array values, int count) GetKeyValuePairs() { var self = (godot_dictionary)NativeValue; @@ -189,6 +200,9 @@ namespace Godot.Collections NativeFuncs.godotsharp_dictionary_add(ref self, variantKey, variantValue); } + void ICollection>.Add(KeyValuePair item) + => Add(item.Key, item.Value); + /// /// Erases all items from this . /// @@ -203,28 +217,72 @@ namespace Godot.Collections /// /// The key to look for. /// Whether or not this dictionary contains the given key. - public bool Contains(object key) + public bool ContainsKey(object key) { using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); var self = (godot_dictionary)NativeValue; return NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool(); } - /// - /// Gets an enumerator for this . - /// - /// An enumerator. - public IDictionaryEnumerator GetEnumerator() => new DictionaryEnumerator(this); + public bool Contains(KeyValuePair item) + { + using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(item.Key); + var self = (godot_dictionary)NativeValue; + bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self, + variantKey, out godot_variant retValue).ToBool(); + + using (retValue) + { + if (!found) + return false; + + using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item.Value); + return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool(); + } + } + + bool IDictionary.Contains(object key) + { + throw new NotImplementedException(); + } /// /// Removes an element from this by key. /// /// The key of the element to remove. - public void Remove(object key) + public bool Remove(object key) { using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); var self = (godot_dictionary)NativeValue; - NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey); + return NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey).ToBool(); + } + + public bool Remove(KeyValuePair item) + { + using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(item.Key); + var self = (godot_dictionary)NativeValue; + bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self, + variantKey, out godot_variant retValue).ToBool(); + + using (retValue) + { + if (!found) + return false; + + using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item.Value); + if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool()) + { + return NativeFuncs.godotsharp_dictionary_remove_key( + ref self, variantKey).ToBool(); + } + + return false; + } + } + + void IDictionary.Remove(object key) + { + _ = Remove(key); } // ICollection @@ -247,38 +305,91 @@ namespace Godot.Collections } } + public bool IsReadOnly => false; + + public bool TryGetValue(object key, out object value) + { + using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key); + var self = (godot_dictionary)NativeValue; + bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self, + variantKey, out godot_variant retValue).ToBool(); + + using (retValue) + { + value = found ? Marshaling.ConvertVariantToManagedObject(retValue) : default; + } + + return found; + } + /// - /// Copies the elements of this to the given - /// untyped C# array, starting at the given index. + /// Copies the elements of this to the given untyped + /// array, starting at the given index. /// /// The array to copy to. - /// The index to start at. - public void CopyTo(System.Array array, int index) + /// The index to start at. + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException(nameof(array), "Value cannot be null."); + + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException(nameof(arrayIndex), + "Number was less than the array's lower bound in the first dimension."); + + var (keys, values, count) = GetKeyValuePairs(); + + if (array.Length < (arrayIndex + count)) + throw new ArgumentException( + "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + + for (int i = 0; i < count; i++) + { + array[arrayIndex] = new(keys[i], values[i]); + arrayIndex++; + } + } + + void ICollection.CopyTo(System.Array array, int arrayIndex) { if (array == null) throw new ArgumentNullException(nameof(array), "Value cannot be null."); - if (index < 0) - throw new ArgumentOutOfRangeException(nameof(index), + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); var (keys, values, count) = GetKeyValuePairs(); - if (array.Length < (index + count)) + if (array.Length < (arrayIndex + count)) throw new ArgumentException( "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); for (int i = 0; i < count; i++) { - array.SetValue(new DictionaryEntry(keys[i], values[i]), index); - index++; + array.SetValue(new DictionaryEntry(keys[i], values[i]), arrayIndex); + arrayIndex++; } } // IEnumerable + /// + /// Gets an enumerator for this . + /// + /// An enumerator. + public IEnumerator> GetEnumerator() + { + for (int i = 0; i < Count; i++) + { + yield return GetKeyValuePair(i); + } + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IDictionaryEnumerator IDictionary.GetEnumerator() => new DictionaryEnumerator(this); + private class DictionaryEnumerator : IDictionaryEnumerator { private readonly Dictionary _dictionary; @@ -343,6 +454,20 @@ namespace Godot.Collections } } + private KeyValuePair GetKeyValuePair(int index) + { + var self = (godot_dictionary)NativeValue; + NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, index, + out godot_variant key, + out godot_variant value); + using (key) + using (value) + { + return new KeyValuePair(Marshaling.ConvertVariantToManagedObject(key), + Marshaling.ConvertVariantToManagedObject(value)); + } + } + /// /// Converts this to a string. /// @@ -518,8 +643,9 @@ namespace Godot.Collections using (key) using (value) { - return new KeyValuePair((TKey)Marshaling.ConvertVariantToManagedObject(key), - (TValue)Marshaling.ConvertVariantToManagedObject(value)); + return new KeyValuePair( + (TKey)Marshaling.ConvertVariantToManagedObjectOfType(key, TypeOfKeys), + (TValue)Marshaling.ConvertVariantToManagedObjectOfType(value, TypeOfValues)); } } @@ -541,7 +667,7 @@ namespace Godot.Collections /// Whether or not this dictionary contains the given key. public bool ContainsKey(TKey key) { - return _underlyingDict.Contains(key); + return _underlyingDict.ContainsKey(key); } /// @@ -621,21 +747,7 @@ namespace Godot.Collections } bool ICollection>.Contains(KeyValuePair item) - { - using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(item.Key); - var self = (godot_dictionary)_underlyingDict.NativeValue; - bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self, - variantKey, out godot_variant retValue).ToBool(); - - using (retValue) - { - if (!found) - return false; - - using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item.Value); - return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool(); - } - } + => _underlyingDict.Contains(new(item.Key, item.Value)); /// /// Copies the elements of this to the given @@ -666,27 +778,7 @@ namespace Godot.Collections } bool ICollection>.Remove(KeyValuePair item) - { - using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(item.Key); - var self = (godot_dictionary)_underlyingDict.NativeValue; - bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self, - variantKey, out godot_variant retValue).ToBool(); - - using (retValue) - { - if (!found) - return false; - - using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item.Value); - if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool()) - { - return NativeFuncs.godotsharp_dictionary_remove_key( - ref self, variantKey).ToBool(); - } - - return false; - } - } + => _underlyingDict.Remove(new(item.Key, item.Value)); // IEnumerable> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index 1a0d9946d2..563af91ae5 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -686,7 +686,7 @@ namespace Godot.NativeInterop /* capacity: */ godotDictionary.Count }, null)!; - foreach (System.Collections.DictionaryEntry pair in godotDictionary) + foreach (KeyValuePair pair in godotDictionary) dictionary.Add(pair.Key, pair.Value); return dictionary; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs index 72be871f16..7aa27c2867 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -703,8 +703,8 @@ namespace Godot.NativeInterop var res = new System.Collections.Generic.Dictionary(godotDictionary.Count); - foreach (System.Collections.Generic.KeyValuePair pair in godotDictionary) - res.Add(pair.Key, pair.Value); + foreach (System.Collections.Generic.KeyValuePair pair in godotDictionary) + res.Add((TKey)pair.Key, (TValue)pair.Value); return res; } -- cgit v1.2.3