Array.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections;
  4. using System.Diagnostics.CodeAnalysis;
  5. using System.Runtime.CompilerServices;
  6. using Godot.NativeInterop;
  7. namespace Godot.Collections
  8. {
  9. /// <summary>
  10. /// Wrapper around Godot's Array class, an array of Variant
  11. /// typed elements allocated in the engine in C++. Useful when
  12. /// interfacing with the engine. Otherwise prefer .NET collections
  13. /// such as <see cref="System.Array"/> or <see cref="List{T}"/>.
  14. /// </summary>
  15. public sealed class Array :
  16. IList<Variant>,
  17. IReadOnlyList<Variant>,
  18. ICollection,
  19. IDisposable
  20. {
  21. internal godot_array.movable NativeValue;
  22. private WeakReference<IDisposable> _weakReferenceToSelf;
  23. /// <summary>
  24. /// Constructs a new empty <see cref="Array"/>.
  25. /// </summary>
  26. public Array()
  27. {
  28. NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
  29. _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
  30. }
  31. /// <summary>
  32. /// Constructs a new <see cref="Array"/> from the given collection's elements.
  33. /// </summary>
  34. /// <param name="collection">The collection of elements to construct from.</param>
  35. /// <returns>A new Godot Array.</returns>
  36. public Array(IEnumerable<Variant> collection) : this()
  37. {
  38. if (collection == null)
  39. throw new ArgumentNullException(nameof(collection));
  40. foreach (Variant element in collection)
  41. Add(element);
  42. }
  43. /// <summary>
  44. /// Constructs a new <see cref="Array"/> from the given objects.
  45. /// </summary>
  46. /// <param name="array">The objects to put in the new array.</param>
  47. /// <returns>A new Godot Array.</returns>
  48. public Array(Variant[] array) : this()
  49. {
  50. if (array == null)
  51. throw new ArgumentNullException(nameof(array));
  52. NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
  53. _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
  54. int length = array.Length;
  55. Resize(length);
  56. for (int i = 0; i < length; i++)
  57. this[i] = array[i];
  58. }
  59. public Array(Span<StringName> array) : this()
  60. {
  61. if (array == null)
  62. throw new ArgumentNullException(nameof(array));
  63. NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
  64. _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
  65. int length = array.Length;
  66. Resize(length);
  67. for (int i = 0; i < length; i++)
  68. this[i] = array[i];
  69. }
  70. public Array(Span<NodePath> array) : this()
  71. {
  72. if (array == null)
  73. throw new ArgumentNullException(nameof(array));
  74. NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
  75. _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
  76. int length = array.Length;
  77. Resize(length);
  78. for (int i = 0; i < length; i++)
  79. this[i] = array[i];
  80. }
  81. public Array(Span<RID> array) : this()
  82. {
  83. if (array == null)
  84. throw new ArgumentNullException(nameof(array));
  85. NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
  86. _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
  87. int length = array.Length;
  88. Resize(length);
  89. for (int i = 0; i < length; i++)
  90. this[i] = array[i];
  91. }
  92. // We must use ReadOnlySpan instead of Span here as this can accept implicit conversions
  93. // from derived types (e.g.: Node[]). Implicit conversion from Derived[] to Base[] are
  94. // fine as long as the array is not mutated. However, Span does this type checking at
  95. // instantiation, so it's not possible to use it even when not mutating anything.
  96. // ReSharper disable once RedundantNameQualifier
  97. public Array(ReadOnlySpan<Godot.Object> array) : this()
  98. {
  99. if (array == null)
  100. throw new ArgumentNullException(nameof(array));
  101. NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
  102. _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
  103. int length = array.Length;
  104. Resize(length);
  105. for (int i = 0; i < length; i++)
  106. this[i] = array[i];
  107. }
  108. private Array(godot_array nativeValueToOwn)
  109. {
  110. NativeValue = (godot_array.movable)(nativeValueToOwn.IsAllocated ?
  111. nativeValueToOwn :
  112. NativeFuncs.godotsharp_array_new());
  113. _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
  114. }
  115. // Explicit name to make it very clear
  116. internal static Array CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn)
  117. => new Array(nativeValueToOwn);
  118. ~Array()
  119. {
  120. Dispose(false);
  121. }
  122. /// <summary>
  123. /// Disposes of this <see cref="Array"/>.
  124. /// </summary>
  125. public void Dispose()
  126. {
  127. Dispose(true);
  128. GC.SuppressFinalize(this);
  129. }
  130. public void Dispose(bool disposing)
  131. {
  132. // Always dispose `NativeValue` even if disposing is true
  133. NativeValue.DangerousSelfRef.Dispose();
  134. if (_weakReferenceToSelf != null)
  135. {
  136. DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
  137. }
  138. }
  139. /// <summary>
  140. /// Duplicates this <see cref="Array"/>.
  141. /// </summary>
  142. /// <param name="deep">If <see langword="true"/>, performs a deep copy.</param>
  143. /// <returns>A new Godot Array.</returns>
  144. public Array Duplicate(bool deep = false)
  145. {
  146. godot_array newArray;
  147. var self = (godot_array)NativeValue;
  148. NativeFuncs.godotsharp_array_duplicate(ref self, deep.ToGodotBool(), out newArray);
  149. return CreateTakingOwnershipOfDisposableValue(newArray);
  150. }
  151. /// <summary>
  152. /// Resizes this <see cref="Array"/> to the given size.
  153. /// </summary>
  154. /// <param name="newSize">The new size of the array.</param>
  155. /// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns>
  156. public Error Resize(int newSize)
  157. {
  158. var self = (godot_array)NativeValue;
  159. return NativeFuncs.godotsharp_array_resize(ref self, newSize);
  160. }
  161. /// <summary>
  162. /// Shuffles the contents of this <see cref="Array"/> into a random order.
  163. /// </summary>
  164. public void Shuffle()
  165. {
  166. var self = (godot_array)NativeValue;
  167. NativeFuncs.godotsharp_array_shuffle(ref self);
  168. }
  169. /// <summary>
  170. /// Concatenates these two <see cref="Array"/>s.
  171. /// </summary>
  172. /// <param name="left">The first array.</param>
  173. /// <param name="right">The second array.</param>
  174. /// <returns>A new Godot Array with the contents of both arrays.</returns>
  175. public static Array operator +(Array left, Array right)
  176. {
  177. if (left == null)
  178. {
  179. if (right == null)
  180. return new Array();
  181. return right.Duplicate(deep: false);
  182. }
  183. if (right == null)
  184. return left.Duplicate(deep: false);
  185. int leftCount = left.Count;
  186. int rightCount = right.Count;
  187. Array newArray = left.Duplicate(deep: false);
  188. newArray.Resize(leftCount + rightCount);
  189. for (int i = 0; i < rightCount; i++)
  190. newArray[i + leftCount] = right[i];
  191. return newArray;
  192. }
  193. /// <summary>
  194. /// Returns the item at the given <paramref name="index"/>.
  195. /// </summary>
  196. /// <value>The <see cref="Variant"/> item at the given <paramref name="index"/>.</value>
  197. public unsafe Variant this[int index]
  198. {
  199. get
  200. {
  201. GetVariantBorrowElementAt(index, out godot_variant borrowElem);
  202. return Variant.CreateCopyingBorrowed(borrowElem);
  203. }
  204. set
  205. {
  206. if (index < 0 || index >= Count)
  207. throw new ArgumentOutOfRangeException(nameof(index));
  208. var self = (godot_array)NativeValue;
  209. godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self);
  210. godot_variant* itemPtr = &ptrw[index];
  211. (*itemPtr).Dispose();
  212. *itemPtr = value.CopyNativeVariant();
  213. }
  214. }
  215. /// <summary>
  216. /// Adds an item to the end of this <see cref="Array"/>.
  217. /// This is the same as <c>append</c> or <c>push_back</c> in GDScript.
  218. /// </summary>
  219. /// <param name="item">The <see cref="Variant"/> item to add.</param>
  220. public void Add(Variant item)
  221. {
  222. godot_variant variantValue = (godot_variant)item.NativeVar;
  223. var self = (godot_array)NativeValue;
  224. _ = NativeFuncs.godotsharp_array_add(ref self, variantValue);
  225. }
  226. /// <summary>
  227. /// Checks if this <see cref="Array"/> contains the given item.
  228. /// </summary>
  229. /// <param name="item">The <see cref="Variant"/> item to look for.</param>
  230. /// <returns>Whether or not this array contains the given item.</returns>
  231. public bool Contains(Variant item) => IndexOf(item) != -1;
  232. /// <summary>
  233. /// Erases all items from this <see cref="Array"/>.
  234. /// </summary>
  235. public void Clear() => Resize(0);
  236. /// <summary>
  237. /// Searches this <see cref="Array"/> for an item
  238. /// and returns its index or -1 if not found.
  239. /// </summary>
  240. /// <param name="item">The <see cref="Variant"/> item to search for.</param>
  241. /// <returns>The index of the item, or -1 if not found.</returns>
  242. public int IndexOf(Variant item)
  243. {
  244. godot_variant variantValue = (godot_variant)item.NativeVar;
  245. var self = (godot_array)NativeValue;
  246. return NativeFuncs.godotsharp_array_index_of(ref self, variantValue);
  247. }
  248. /// <summary>
  249. /// Inserts a new item at a given position in the array.
  250. /// The position must be a valid position of an existing item,
  251. /// or the position at the end of the array.
  252. /// Existing items will be moved to the right.
  253. /// </summary>
  254. /// <param name="index">The index to insert at.</param>
  255. /// <param name="item">The <see cref="Variant"/> item to insert.</param>
  256. public void Insert(int index, Variant item)
  257. {
  258. if (index < 0 || index > Count)
  259. throw new ArgumentOutOfRangeException(nameof(index));
  260. godot_variant variantValue = (godot_variant)item.NativeVar;
  261. var self = (godot_array)NativeValue;
  262. NativeFuncs.godotsharp_array_insert(ref self, index, variantValue);
  263. }
  264. /// <summary>
  265. /// Removes the first occurrence of the specified <paramref name="item"/>
  266. /// from this <see cref="Array"/>.
  267. /// </summary>
  268. /// <param name="item">The value to remove.</param>
  269. public bool Remove(Variant item)
  270. {
  271. int index = IndexOf(item);
  272. if (index >= 0)
  273. {
  274. RemoveAt(index);
  275. return true;
  276. }
  277. return false;
  278. }
  279. /// <summary>
  280. /// Removes an element from this <see cref="Array"/> by index.
  281. /// </summary>
  282. /// <param name="index">The index of the element to remove.</param>
  283. public void RemoveAt(int index)
  284. {
  285. if (index < 0 || index > Count)
  286. throw new ArgumentOutOfRangeException(nameof(index));
  287. var self = (godot_array)NativeValue;
  288. NativeFuncs.godotsharp_array_remove_at(ref self, index);
  289. }
  290. // ICollection
  291. /// <summary>
  292. /// Returns the number of elements in this <see cref="Array"/>.
  293. /// This is also known as the size or length of the array.
  294. /// </summary>
  295. /// <returns>The number of elements.</returns>
  296. public int Count => NativeValue.DangerousSelfRef.Size;
  297. bool ICollection.IsSynchronized => false;
  298. object ICollection.SyncRoot => false;
  299. bool ICollection<Variant>.IsReadOnly => false;
  300. /// <summary>
  301. /// Copies the elements of this <see cref="Array"/> to the given
  302. /// <see cref="Variant"/> C# array, starting at the given index.
  303. /// </summary>
  304. /// <param name="array">The array to copy to.</param>
  305. /// <param name="arrayIndex">The index to start at.</param>
  306. public void CopyTo(Variant[] array, int arrayIndex)
  307. {
  308. if (array == null)
  309. throw new ArgumentNullException(nameof(array), "Value cannot be null.");
  310. if (arrayIndex < 0)
  311. {
  312. throw new ArgumentOutOfRangeException(nameof(arrayIndex),
  313. "Number was less than the array's lower bound in the first dimension.");
  314. }
  315. int count = Count;
  316. if (array.Length < (arrayIndex + count))
  317. {
  318. throw new ArgumentException(
  319. "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
  320. }
  321. unsafe
  322. {
  323. for (int i = 0; i < count; i++)
  324. {
  325. array[arrayIndex] = Variant.CreateCopyingBorrowed(NativeValue.DangerousSelfRef.Elements[i]);
  326. arrayIndex++;
  327. }
  328. }
  329. }
  330. void ICollection.CopyTo(System.Array array, int index)
  331. {
  332. if (array == null)
  333. throw new ArgumentNullException(nameof(array), "Value cannot be null.");
  334. if (index < 0)
  335. {
  336. throw new ArgumentOutOfRangeException(nameof(index),
  337. "Number was less than the array's lower bound in the first dimension.");
  338. }
  339. int count = Count;
  340. if (array.Length < (index + count))
  341. {
  342. throw new ArgumentException(
  343. "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
  344. }
  345. unsafe
  346. {
  347. for (int i = 0; i < count; i++)
  348. {
  349. object obj = Marshaling.ConvertVariantToManagedObject(NativeValue.DangerousSelfRef.Elements[i]);
  350. array.SetValue(obj, index);
  351. index++;
  352. }
  353. }
  354. }
  355. // IEnumerable
  356. /// <summary>
  357. /// Gets an enumerator for this <see cref="Array"/>.
  358. /// </summary>
  359. /// <returns>An enumerator.</returns>
  360. public IEnumerator<Variant> GetEnumerator()
  361. {
  362. int count = Count;
  363. for (int i = 0; i < count; i++)
  364. {
  365. yield return this[i];
  366. }
  367. }
  368. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
  369. /// <summary>
  370. /// Converts this <see cref="Array"/> to a string.
  371. /// </summary>
  372. /// <returns>A string representation of this array.</returns>
  373. public override string ToString()
  374. {
  375. var self = (godot_array)NativeValue;
  376. NativeFuncs.godotsharp_array_to_string(ref self, out godot_string str);
  377. using (str)
  378. return Marshaling.ConvertStringToManaged(str);
  379. }
  380. /// <summary>
  381. /// The variant returned via the <paramref name="elem"/> parameter is owned by the Array and must not be disposed.
  382. /// </summary>
  383. internal void GetVariantBorrowElementAt(int index, out godot_variant elem)
  384. {
  385. if (index < 0 || index >= Count)
  386. throw new ArgumentOutOfRangeException(nameof(index));
  387. GetVariantBorrowElementAtUnchecked(index, out elem);
  388. }
  389. /// <summary>
  390. /// The variant returned via the <paramref name="elem"/> parameter is owned by the Array and must not be disposed.
  391. /// </summary>
  392. internal unsafe void GetVariantBorrowElementAtUnchecked(int index, out godot_variant elem)
  393. {
  394. elem = NativeValue.DangerousSelfRef.Elements[index];
  395. }
  396. }
  397. /// <summary>
  398. /// Typed wrapper around Godot's Array class, an array of Variant
  399. /// typed elements allocated in the engine in C++. Useful when
  400. /// interfacing with the engine. Otherwise prefer .NET collections
  401. /// such as arrays or <see cref="List{T}"/>.
  402. /// </summary>
  403. /// <typeparam name="T">The type of the array.</typeparam>
  404. [SuppressMessage("ReSharper", "RedundantExtendsListEntry")]
  405. [SuppressMessage("Naming", "CA1710", MessageId = "Identifiers should have correct suffix")]
  406. public sealed class Array<[MustBeVariant] T> :
  407. IList<T>,
  408. IReadOnlyList<T>,
  409. ICollection<T>,
  410. IEnumerable<T>
  411. {
  412. // ReSharper disable StaticMemberInGenericType
  413. // Warning is about unique static fields being created for each generic type combination:
  414. // https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html
  415. // In our case this is exactly what we want.
  416. private static unsafe delegate* managed<in T, godot_variant> _convertToVariantCallback;
  417. private static unsafe delegate* managed<in godot_variant, T> _convertToManagedCallback;
  418. // ReSharper restore StaticMemberInGenericType
  419. static unsafe Array()
  420. {
  421. _convertToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<T>();
  422. _convertToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<T>();
  423. }
  424. private static unsafe void ValidateVariantConversionCallbacks()
  425. {
  426. if (_convertToVariantCallback == null || _convertToManagedCallback == null)
  427. {
  428. throw new InvalidOperationException(
  429. $"The array element type is not supported for conversion to Variant: '{typeof(T).FullName}'.");
  430. }
  431. }
  432. private readonly Array _underlyingArray;
  433. internal ref godot_array.movable NativeValue
  434. {
  435. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  436. get => ref _underlyingArray.NativeValue;
  437. }
  438. /// <summary>
  439. /// Constructs a new empty <see cref="Array{T}"/>.
  440. /// </summary>
  441. public Array()
  442. {
  443. ValidateVariantConversionCallbacks();
  444. _underlyingArray = new Array();
  445. }
  446. /// <summary>
  447. /// Constructs a new <see cref="Array{T}"/> from the given collection's elements.
  448. /// </summary>
  449. /// <param name="collection">The collection of elements to construct from.</param>
  450. /// <returns>A new Godot Array.</returns>
  451. public Array(IEnumerable<T> collection)
  452. {
  453. ValidateVariantConversionCallbacks();
  454. if (collection == null)
  455. throw new ArgumentNullException(nameof(collection));
  456. _underlyingArray = new Array();
  457. foreach (T element in collection)
  458. Add(element);
  459. }
  460. /// <summary>
  461. /// Constructs a new <see cref="Array{T}"/> from the given items.
  462. /// </summary>
  463. /// <param name="array">The items to put in the new array.</param>
  464. /// <returns>A new Godot Array.</returns>
  465. public Array(T[] array) : this()
  466. {
  467. ValidateVariantConversionCallbacks();
  468. if (array == null)
  469. throw new ArgumentNullException(nameof(array));
  470. _underlyingArray = new Array();
  471. foreach (T element in array)
  472. Add(element);
  473. }
  474. /// <summary>
  475. /// Constructs a typed <see cref="Array{T}"/> from an untyped <see cref="Array"/>.
  476. /// </summary>
  477. /// <param name="array">The untyped array to construct from.</param>
  478. public Array(Array array)
  479. {
  480. ValidateVariantConversionCallbacks();
  481. _underlyingArray = array;
  482. }
  483. // Explicit name to make it very clear
  484. internal static Array<T> CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn)
  485. => new Array<T>(Array.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn));
  486. /// <summary>
  487. /// Converts this typed <see cref="Array{T}"/> to an untyped <see cref="Array"/>.
  488. /// </summary>
  489. /// <param name="from">The typed array to convert.</param>
  490. public static explicit operator Array(Array<T> from)
  491. {
  492. return from?._underlyingArray;
  493. }
  494. /// <summary>
  495. /// Duplicates this <see cref="Array{T}"/>.
  496. /// </summary>
  497. /// <param name="deep">If <see langword="true"/>, performs a deep copy.</param>
  498. /// <returns>A new Godot Array.</returns>
  499. public Array<T> Duplicate(bool deep = false)
  500. {
  501. return new Array<T>(_underlyingArray.Duplicate(deep));
  502. }
  503. /// <summary>
  504. /// Resizes this <see cref="Array{T}"/> to the given size.
  505. /// </summary>
  506. /// <param name="newSize">The new size of the array.</param>
  507. /// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns>
  508. public Error Resize(int newSize)
  509. {
  510. return _underlyingArray.Resize(newSize);
  511. }
  512. /// <summary>
  513. /// Shuffles the contents of this <see cref="Array{T}"/> into a random order.
  514. /// </summary>
  515. public void Shuffle()
  516. {
  517. _underlyingArray.Shuffle();
  518. }
  519. /// <summary>
  520. /// Concatenates these two <see cref="Array{T}"/>s.
  521. /// </summary>
  522. /// <param name="left">The first array.</param>
  523. /// <param name="right">The second array.</param>
  524. /// <returns>A new Godot Array with the contents of both arrays.</returns>
  525. public static Array<T> operator +(Array<T> left, Array<T> right)
  526. {
  527. if (left == null)
  528. {
  529. if (right == null)
  530. return new Array<T>();
  531. return right.Duplicate(deep: false);
  532. }
  533. if (right == null)
  534. return left.Duplicate(deep: false);
  535. return new Array<T>(left._underlyingArray + right._underlyingArray);
  536. }
  537. // IList<T>
  538. /// <summary>
  539. /// Returns the value at the given <paramref name="index"/>.
  540. /// </summary>
  541. /// <value>The value at the given <paramref name="index"/>.</value>
  542. public unsafe T this[int index]
  543. {
  544. get
  545. {
  546. _underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem);
  547. return _convertToManagedCallback(borrowElem);
  548. }
  549. set
  550. {
  551. if (index < 0 || index >= Count)
  552. throw new ArgumentOutOfRangeException(nameof(index));
  553. var self = (godot_array)_underlyingArray.NativeValue;
  554. godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self);
  555. godot_variant* itemPtr = &ptrw[index];
  556. (*itemPtr).Dispose();
  557. *itemPtr = _convertToVariantCallback(value);
  558. }
  559. }
  560. /// <summary>
  561. /// Searches this <see cref="Array{T}"/> for an item
  562. /// and returns its index or -1 if not found.
  563. /// </summary>
  564. /// <param name="item">The item to search for.</param>
  565. /// <returns>The index of the item, or -1 if not found.</returns>
  566. public unsafe int IndexOf(T item)
  567. {
  568. using var variantValue = _convertToVariantCallback(item);
  569. var self = (godot_array)_underlyingArray.NativeValue;
  570. return NativeFuncs.godotsharp_array_index_of(ref self, variantValue);
  571. }
  572. /// <summary>
  573. /// Inserts a new item at a given position in the <see cref="Array{T}"/>.
  574. /// The position must be a valid position of an existing item,
  575. /// or the position at the end of the array.
  576. /// Existing items will be moved to the right.
  577. /// </summary>
  578. /// <param name="index">The index to insert at.</param>
  579. /// <param name="item">The item to insert.</param>
  580. public unsafe void Insert(int index, T item)
  581. {
  582. if (index < 0 || index > Count)
  583. throw new ArgumentOutOfRangeException(nameof(index));
  584. using var variantValue = _convertToVariantCallback(item);
  585. var self = (godot_array)_underlyingArray.NativeValue;
  586. NativeFuncs.godotsharp_array_insert(ref self, index, variantValue);
  587. }
  588. /// <summary>
  589. /// Removes an element from this <see cref="Array{T}"/> by index.
  590. /// </summary>
  591. /// <param name="index">The index of the element to remove.</param>
  592. public void RemoveAt(int index)
  593. {
  594. _underlyingArray.RemoveAt(index);
  595. }
  596. // ICollection<T>
  597. /// <summary>
  598. /// Returns the number of elements in this <see cref="Array{T}"/>.
  599. /// This is also known as the size or length of the array.
  600. /// </summary>
  601. /// <returns>The number of elements.</returns>
  602. public int Count => _underlyingArray.Count;
  603. bool ICollection<T>.IsReadOnly => false;
  604. /// <summary>
  605. /// Adds an item to the end of this <see cref="Array{T}"/>.
  606. /// This is the same as <c>append</c> or <c>push_back</c> in GDScript.
  607. /// </summary>
  608. /// <param name="item">The item to add.</param>
  609. /// <returns>The new size after adding the item.</returns>
  610. public unsafe void Add(T item)
  611. {
  612. using var variantValue = _convertToVariantCallback(item);
  613. var self = (godot_array)_underlyingArray.NativeValue;
  614. _ = NativeFuncs.godotsharp_array_add(ref self, variantValue);
  615. }
  616. /// <summary>
  617. /// Erases all items from this <see cref="Array{T}"/>.
  618. /// </summary>
  619. public void Clear()
  620. {
  621. _underlyingArray.Clear();
  622. }
  623. /// <summary>
  624. /// Checks if this <see cref="Array{T}"/> contains the given item.
  625. /// </summary>
  626. /// <param name="item">The item to look for.</param>
  627. /// <returns>Whether or not this array contains the given item.</returns>
  628. public bool Contains(T item) => IndexOf(item) != -1;
  629. /// <summary>
  630. /// Copies the elements of this <see cref="Array{T}"/> to the given
  631. /// C# array, starting at the given index.
  632. /// </summary>
  633. /// <param name="array">The C# array to copy to.</param>
  634. /// <param name="arrayIndex">The index to start at.</param>
  635. public void CopyTo(T[] array, int arrayIndex)
  636. {
  637. if (array == null)
  638. throw new ArgumentNullException(nameof(array), "Value cannot be null.");
  639. if (arrayIndex < 0)
  640. {
  641. throw new ArgumentOutOfRangeException(nameof(arrayIndex),
  642. "Number was less than the array's lower bound in the first dimension.");
  643. }
  644. int count = Count;
  645. if (array.Length < (arrayIndex + count))
  646. {
  647. throw new ArgumentException(
  648. "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
  649. }
  650. for (int i = 0; i < count; i++)
  651. {
  652. array[arrayIndex] = this[i];
  653. arrayIndex++;
  654. }
  655. }
  656. /// <summary>
  657. /// Removes the first occurrence of the specified value
  658. /// from this <see cref="Array{T}"/>.
  659. /// </summary>
  660. /// <param name="item">The value to remove.</param>
  661. /// <returns>A <see langword="bool"/> indicating success or failure.</returns>
  662. public bool Remove(T item)
  663. {
  664. int index = IndexOf(item);
  665. if (index >= 0)
  666. {
  667. RemoveAt(index);
  668. return true;
  669. }
  670. return false;
  671. }
  672. // IEnumerable<T>
  673. /// <summary>
  674. /// Gets an enumerator for this <see cref="Array{T}"/>.
  675. /// </summary>
  676. /// <returns>An enumerator.</returns>
  677. public IEnumerator<T> GetEnumerator()
  678. {
  679. int count = _underlyingArray.Count;
  680. for (int i = 0; i < count; i++)
  681. {
  682. yield return this[i];
  683. }
  684. }
  685. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
  686. /// <summary>
  687. /// Converts this <see cref="Array{T}"/> to a string.
  688. /// </summary>
  689. /// <returns>A string representation of this array.</returns>
  690. public override string ToString() => _underlyingArray.ToString();
  691. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  692. public static implicit operator Variant(Array<T> from) => Variant.CreateFrom(from);
  693. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  694. public static explicit operator Array<T>(Variant from) => from.AsGodotArray<T>();
  695. }
  696. }