Array.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939
  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<GodotObject> 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. /// <exception cref="InvalidOperationException">
  155. /// The array is read-only.
  156. /// </exception>
  157. /// <param name="newSize">The new size of the array.</param>
  158. /// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns>
  159. public Error Resize(int newSize)
  160. {
  161. ThrowIfReadOnly();
  162. var self = (godot_array)NativeValue;
  163. return NativeFuncs.godotsharp_array_resize(ref self, newSize);
  164. }
  165. /// <summary>
  166. /// Shuffles the contents of this <see cref="Array"/> into a random order.
  167. /// </summary>
  168. /// <exception cref="InvalidOperationException">
  169. /// The array is read-only.
  170. /// </exception>
  171. public void Shuffle()
  172. {
  173. ThrowIfReadOnly();
  174. var self = (godot_array)NativeValue;
  175. NativeFuncs.godotsharp_array_shuffle(ref self);
  176. }
  177. /// <summary>
  178. /// Concatenates these two <see cref="Array"/>s.
  179. /// </summary>
  180. /// <param name="left">The first array.</param>
  181. /// <param name="right">The second array.</param>
  182. /// <returns>A new Godot Array with the contents of both arrays.</returns>
  183. public static Array operator +(Array left, Array right)
  184. {
  185. if (left == null)
  186. {
  187. if (right == null)
  188. return new Array();
  189. return right.Duplicate(deep: false);
  190. }
  191. if (right == null)
  192. return left.Duplicate(deep: false);
  193. int leftCount = left.Count;
  194. int rightCount = right.Count;
  195. Array newArray = left.Duplicate(deep: false);
  196. newArray.Resize(leftCount + rightCount);
  197. for (int i = 0; i < rightCount; i++)
  198. newArray[i + leftCount] = right[i];
  199. return newArray;
  200. }
  201. /// <summary>
  202. /// Returns the item at the given <paramref name="index"/>.
  203. /// </summary>
  204. /// <exception cref="InvalidOperationException">
  205. /// The property is assigned and the array is read-only.
  206. /// </exception>
  207. /// <value>The <see cref="Variant"/> item at the given <paramref name="index"/>.</value>
  208. public unsafe Variant this[int index]
  209. {
  210. get
  211. {
  212. GetVariantBorrowElementAt(index, out godot_variant borrowElem);
  213. return Variant.CreateCopyingBorrowed(borrowElem);
  214. }
  215. set
  216. {
  217. ThrowIfReadOnly();
  218. if (index < 0 || index >= Count)
  219. throw new ArgumentOutOfRangeException(nameof(index));
  220. var self = (godot_array)NativeValue;
  221. godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self);
  222. godot_variant* itemPtr = &ptrw[index];
  223. (*itemPtr).Dispose();
  224. *itemPtr = value.CopyNativeVariant();
  225. }
  226. }
  227. /// <summary>
  228. /// Adds an item to the end of this <see cref="Array"/>.
  229. /// This is the same as <c>append</c> or <c>push_back</c> in GDScript.
  230. /// </summary>
  231. /// <exception cref="InvalidOperationException">
  232. /// The array is read-only.
  233. /// </exception>
  234. /// <param name="item">The <see cref="Variant"/> item to add.</param>
  235. public void Add(Variant item)
  236. {
  237. ThrowIfReadOnly();
  238. godot_variant variantValue = (godot_variant)item.NativeVar;
  239. var self = (godot_array)NativeValue;
  240. _ = NativeFuncs.godotsharp_array_add(ref self, variantValue);
  241. }
  242. /// <summary>
  243. /// Checks if this <see cref="Array"/> contains the given item.
  244. /// </summary>
  245. /// <param name="item">The <see cref="Variant"/> item to look for.</param>
  246. /// <returns>Whether or not this array contains the given item.</returns>
  247. public bool Contains(Variant item) => IndexOf(item) != -1;
  248. /// <summary>
  249. /// Erases all items from this <see cref="Array"/>.
  250. /// </summary>
  251. /// <exception cref="InvalidOperationException">
  252. /// The array is read-only.
  253. /// </exception>
  254. public void Clear() => Resize(0);
  255. /// <summary>
  256. /// Searches this <see cref="Array"/> for an item
  257. /// and returns its index or -1 if not found.
  258. /// </summary>
  259. /// <param name="item">The <see cref="Variant"/> item to search for.</param>
  260. /// <returns>The index of the item, or -1 if not found.</returns>
  261. public int IndexOf(Variant item)
  262. {
  263. godot_variant variantValue = (godot_variant)item.NativeVar;
  264. var self = (godot_array)NativeValue;
  265. return NativeFuncs.godotsharp_array_index_of(ref self, variantValue);
  266. }
  267. /// <summary>
  268. /// Inserts a new item at a given position in the array.
  269. /// The position must be a valid position of an existing item,
  270. /// or the position at the end of the array.
  271. /// Existing items will be moved to the right.
  272. /// </summary>
  273. /// <exception cref="InvalidOperationException">
  274. /// The array is read-only.
  275. /// </exception>
  276. /// <param name="index">The index to insert at.</param>
  277. /// <param name="item">The <see cref="Variant"/> item to insert.</param>
  278. public void Insert(int index, Variant item)
  279. {
  280. ThrowIfReadOnly();
  281. if (index < 0 || index > Count)
  282. throw new ArgumentOutOfRangeException(nameof(index));
  283. godot_variant variantValue = (godot_variant)item.NativeVar;
  284. var self = (godot_array)NativeValue;
  285. NativeFuncs.godotsharp_array_insert(ref self, index, variantValue);
  286. }
  287. /// <summary>
  288. /// Removes the first occurrence of the specified <paramref name="item"/>
  289. /// from this <see cref="Array"/>.
  290. /// </summary>
  291. /// <exception cref="InvalidOperationException">
  292. /// The array is read-only.
  293. /// </exception>
  294. /// <param name="item">The value to remove.</param>
  295. public bool Remove(Variant item)
  296. {
  297. ThrowIfReadOnly();
  298. int index = IndexOf(item);
  299. if (index >= 0)
  300. {
  301. RemoveAt(index);
  302. return true;
  303. }
  304. return false;
  305. }
  306. /// <summary>
  307. /// Removes an element from this <see cref="Array"/> by index.
  308. /// </summary>
  309. /// <exception cref="InvalidOperationException">
  310. /// The array is read-only.
  311. /// </exception>
  312. /// <param name="index">The index of the element to remove.</param>
  313. public void RemoveAt(int index)
  314. {
  315. ThrowIfReadOnly();
  316. if (index < 0 || index > Count)
  317. throw new ArgumentOutOfRangeException(nameof(index));
  318. var self = (godot_array)NativeValue;
  319. NativeFuncs.godotsharp_array_remove_at(ref self, index);
  320. }
  321. // ICollection
  322. /// <summary>
  323. /// Returns the number of elements in this <see cref="Array"/>.
  324. /// This is also known as the size or length of the array.
  325. /// </summary>
  326. /// <returns>The number of elements.</returns>
  327. public int Count => NativeValue.DangerousSelfRef.Size;
  328. bool ICollection.IsSynchronized => false;
  329. object ICollection.SyncRoot => false;
  330. /// <summary>
  331. /// Returns <see langword="true"/> if the array is read-only.
  332. /// See <see cref="MakeReadOnly"/>.
  333. /// </summary>
  334. public bool IsReadOnly => NativeValue.DangerousSelfRef.IsReadOnly;
  335. /// <summary>
  336. /// Makes the <see cref="Array"/> read-only, i.e. disabled modying of the
  337. /// array's elements. Does not apply to nested content, e.g. content of
  338. /// nested arrays.
  339. /// </summary>
  340. public void MakeReadOnly()
  341. {
  342. if (IsReadOnly)
  343. {
  344. // Avoid interop call when the array is already read-only.
  345. return;
  346. }
  347. var self = (godot_array)NativeValue;
  348. NativeFuncs.godotsharp_array_make_read_only(ref self);
  349. }
  350. /// <summary>
  351. /// Copies the elements of this <see cref="Array"/> to the given
  352. /// <see cref="Variant"/> C# array, starting at the given index.
  353. /// </summary>
  354. /// <param name="array">The array to copy to.</param>
  355. /// <param name="arrayIndex">The index to start at.</param>
  356. public void CopyTo(Variant[] array, int arrayIndex)
  357. {
  358. if (array == null)
  359. throw new ArgumentNullException(nameof(array), "Value cannot be null.");
  360. if (arrayIndex < 0)
  361. {
  362. throw new ArgumentOutOfRangeException(nameof(arrayIndex),
  363. "Number was less than the array's lower bound in the first dimension.");
  364. }
  365. int count = Count;
  366. if (array.Length < (arrayIndex + count))
  367. {
  368. throw new ArgumentException(
  369. "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
  370. }
  371. unsafe
  372. {
  373. for (int i = 0; i < count; i++)
  374. {
  375. array[arrayIndex] = Variant.CreateCopyingBorrowed(NativeValue.DangerousSelfRef.Elements[i]);
  376. arrayIndex++;
  377. }
  378. }
  379. }
  380. void ICollection.CopyTo(System.Array array, int index)
  381. {
  382. if (array == null)
  383. throw new ArgumentNullException(nameof(array), "Value cannot be null.");
  384. if (index < 0)
  385. {
  386. throw new ArgumentOutOfRangeException(nameof(index),
  387. "Number was less than the array's lower bound in the first dimension.");
  388. }
  389. int count = Count;
  390. if (array.Length < (index + count))
  391. {
  392. throw new ArgumentException(
  393. "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
  394. }
  395. unsafe
  396. {
  397. for (int i = 0; i < count; i++)
  398. {
  399. object boxedVariant = Variant.CreateCopyingBorrowed(NativeValue.DangerousSelfRef.Elements[i]);
  400. array.SetValue(boxedVariant, index);
  401. index++;
  402. }
  403. }
  404. }
  405. // IEnumerable
  406. /// <summary>
  407. /// Gets an enumerator for this <see cref="Array"/>.
  408. /// </summary>
  409. /// <returns>An enumerator.</returns>
  410. public IEnumerator<Variant> GetEnumerator()
  411. {
  412. int count = Count;
  413. for (int i = 0; i < count; i++)
  414. {
  415. yield return this[i];
  416. }
  417. }
  418. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
  419. /// <summary>
  420. /// Converts this <see cref="Array"/> to a string.
  421. /// </summary>
  422. /// <returns>A string representation of this array.</returns>
  423. public override string ToString()
  424. {
  425. var self = (godot_array)NativeValue;
  426. NativeFuncs.godotsharp_array_to_string(ref self, out godot_string str);
  427. using (str)
  428. return Marshaling.ConvertStringToManaged(str);
  429. }
  430. /// <summary>
  431. /// The variant returned via the <paramref name="elem"/> parameter is owned by the Array and must not be disposed.
  432. /// </summary>
  433. internal void GetVariantBorrowElementAt(int index, out godot_variant elem)
  434. {
  435. if (index < 0 || index >= Count)
  436. throw new ArgumentOutOfRangeException(nameof(index));
  437. GetVariantBorrowElementAtUnchecked(index, out elem);
  438. }
  439. /// <summary>
  440. /// The variant returned via the <paramref name="elem"/> parameter is owned by the Array and must not be disposed.
  441. /// </summary>
  442. internal unsafe void GetVariantBorrowElementAtUnchecked(int index, out godot_variant elem)
  443. {
  444. elem = NativeValue.DangerousSelfRef.Elements[index];
  445. }
  446. private void ThrowIfReadOnly()
  447. {
  448. if (IsReadOnly)
  449. {
  450. throw new InvalidOperationException("Array instance is read-only.");
  451. }
  452. }
  453. }
  454. internal interface IGenericGodotArray
  455. {
  456. public Array UnderlyingArray { get; }
  457. }
  458. /// <summary>
  459. /// Typed wrapper around Godot's Array class, an array of Variant
  460. /// typed elements allocated in the engine in C++. Useful when
  461. /// interfacing with the engine. Otherwise prefer .NET collections
  462. /// such as arrays or <see cref="List{T}"/>.
  463. /// </summary>
  464. /// <typeparam name="T">The type of the array.</typeparam>
  465. [SuppressMessage("ReSharper", "RedundantExtendsListEntry")]
  466. [SuppressMessage("Naming", "CA1710", MessageId = "Identifiers should have correct suffix")]
  467. public sealed class Array<[MustBeVariant] T> :
  468. IList<T>,
  469. IReadOnlyList<T>,
  470. ICollection<T>,
  471. IEnumerable<T>,
  472. IGenericGodotArray
  473. {
  474. private static godot_variant ToVariantFunc(in Array<T> godotArray) =>
  475. VariantUtils.CreateFromArray(godotArray);
  476. private static Array<T> FromVariantFunc(in godot_variant variant) =>
  477. VariantUtils.ConvertToArray<T>(variant);
  478. static unsafe Array()
  479. {
  480. VariantUtils.GenericConversion<Array<T>>.ToVariantCb = &ToVariantFunc;
  481. VariantUtils.GenericConversion<Array<T>>.FromVariantCb = &FromVariantFunc;
  482. }
  483. private readonly Array _underlyingArray;
  484. Array IGenericGodotArray.UnderlyingArray => _underlyingArray;
  485. internal ref godot_array.movable NativeValue
  486. {
  487. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  488. get => ref _underlyingArray.NativeValue;
  489. }
  490. /// <summary>
  491. /// Constructs a new empty <see cref="Array{T}"/>.
  492. /// </summary>
  493. public Array()
  494. {
  495. _underlyingArray = new Array();
  496. }
  497. /// <summary>
  498. /// Constructs a new <see cref="Array{T}"/> from the given collection's elements.
  499. /// </summary>
  500. /// <param name="collection">The collection of elements to construct from.</param>
  501. /// <returns>A new Godot Array.</returns>
  502. public Array(IEnumerable<T> collection)
  503. {
  504. if (collection == null)
  505. throw new ArgumentNullException(nameof(collection));
  506. _underlyingArray = new Array();
  507. foreach (T element in collection)
  508. Add(element);
  509. }
  510. /// <summary>
  511. /// Constructs a new <see cref="Array{T}"/> from the given items.
  512. /// </summary>
  513. /// <param name="array">The items to put in the new array.</param>
  514. /// <returns>A new Godot Array.</returns>
  515. public Array(T[] array) : this()
  516. {
  517. if (array == null)
  518. throw new ArgumentNullException(nameof(array));
  519. _underlyingArray = new Array();
  520. foreach (T element in array)
  521. Add(element);
  522. }
  523. /// <summary>
  524. /// Constructs a typed <see cref="Array{T}"/> from an untyped <see cref="Array"/>.
  525. /// </summary>
  526. /// <param name="array">The untyped array to construct from.</param>
  527. public Array(Array array)
  528. {
  529. _underlyingArray = array;
  530. }
  531. // Explicit name to make it very clear
  532. internal static Array<T> CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn)
  533. => new Array<T>(Array.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn));
  534. /// <summary>
  535. /// Converts this typed <see cref="Array{T}"/> to an untyped <see cref="Array"/>.
  536. /// </summary>
  537. /// <param name="from">The typed array to convert.</param>
  538. public static explicit operator Array(Array<T> from)
  539. {
  540. return from?._underlyingArray;
  541. }
  542. /// <summary>
  543. /// Duplicates this <see cref="Array{T}"/>.
  544. /// </summary>
  545. /// <param name="deep">If <see langword="true"/>, performs a deep copy.</param>
  546. /// <returns>A new Godot Array.</returns>
  547. public Array<T> Duplicate(bool deep = false)
  548. {
  549. return new Array<T>(_underlyingArray.Duplicate(deep));
  550. }
  551. /// <summary>
  552. /// Resizes this <see cref="Array{T}"/> to the given size.
  553. /// </summary>
  554. /// <exception cref="InvalidOperationException">
  555. /// The array is read-only.
  556. /// </exception>
  557. /// <param name="newSize">The new size of the array.</param>
  558. /// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns>
  559. public Error Resize(int newSize)
  560. {
  561. return _underlyingArray.Resize(newSize);
  562. }
  563. /// <summary>
  564. /// Shuffles the contents of this <see cref="Array{T}"/> into a random order.
  565. /// </summary>
  566. /// <exception cref="InvalidOperationException">
  567. /// The array is read-only.
  568. /// </exception>
  569. public void Shuffle()
  570. {
  571. _underlyingArray.Shuffle();
  572. }
  573. /// <summary>
  574. /// Concatenates these two <see cref="Array{T}"/>s.
  575. /// </summary>
  576. /// <param name="left">The first array.</param>
  577. /// <param name="right">The second array.</param>
  578. /// <returns>A new Godot Array with the contents of both arrays.</returns>
  579. public static Array<T> operator +(Array<T> left, Array<T> right)
  580. {
  581. if (left == null)
  582. {
  583. if (right == null)
  584. return new Array<T>();
  585. return right.Duplicate(deep: false);
  586. }
  587. if (right == null)
  588. return left.Duplicate(deep: false);
  589. return new Array<T>(left._underlyingArray + right._underlyingArray);
  590. }
  591. // IList<T>
  592. /// <summary>
  593. /// Returns the value at the given <paramref name="index"/>.
  594. /// </summary>
  595. /// <exception cref="InvalidOperationException">
  596. /// The property is assigned and the array is read-only.
  597. /// </exception>
  598. /// <value>The value at the given <paramref name="index"/>.</value>
  599. public unsafe T this[int index]
  600. {
  601. get
  602. {
  603. _underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem);
  604. return VariantUtils.ConvertTo<T>(borrowElem);
  605. }
  606. set
  607. {
  608. ThrowIfReadOnly();
  609. if (index < 0 || index >= Count)
  610. throw new ArgumentOutOfRangeException(nameof(index));
  611. var self = (godot_array)_underlyingArray.NativeValue;
  612. godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self);
  613. godot_variant* itemPtr = &ptrw[index];
  614. (*itemPtr).Dispose();
  615. *itemPtr = VariantUtils.CreateFrom(value);
  616. }
  617. }
  618. /// <summary>
  619. /// Searches this <see cref="Array{T}"/> for an item
  620. /// and returns its index or -1 if not found.
  621. /// </summary>
  622. /// <param name="item">The item to search for.</param>
  623. /// <returns>The index of the item, or -1 if not found.</returns>
  624. public int IndexOf(T item)
  625. {
  626. using var variantValue = VariantUtils.CreateFrom(item);
  627. var self = (godot_array)_underlyingArray.NativeValue;
  628. return NativeFuncs.godotsharp_array_index_of(ref self, variantValue);
  629. }
  630. /// <summary>
  631. /// Inserts a new item at a given position in the <see cref="Array{T}"/>.
  632. /// The position must be a valid position of an existing item,
  633. /// or the position at the end of the array.
  634. /// Existing items will be moved to the right.
  635. /// </summary>
  636. /// <exception cref="InvalidOperationException">
  637. /// The array is read-only.
  638. /// </exception>
  639. /// <param name="index">The index to insert at.</param>
  640. /// <param name="item">The item to insert.</param>
  641. public void Insert(int index, T item)
  642. {
  643. ThrowIfReadOnly();
  644. if (index < 0 || index > Count)
  645. throw new ArgumentOutOfRangeException(nameof(index));
  646. using var variantValue = VariantUtils.CreateFrom(item);
  647. var self = (godot_array)_underlyingArray.NativeValue;
  648. NativeFuncs.godotsharp_array_insert(ref self, index, variantValue);
  649. }
  650. /// <summary>
  651. /// Removes an element from this <see cref="Array{T}"/> by index.
  652. /// </summary>
  653. /// <exception cref="InvalidOperationException">
  654. /// The array is read-only.
  655. /// </exception>
  656. /// <param name="index">The index of the element to remove.</param>
  657. public void RemoveAt(int index)
  658. {
  659. _underlyingArray.RemoveAt(index);
  660. }
  661. // ICollection<T>
  662. /// <summary>
  663. /// Returns the number of elements in this <see cref="Array{T}"/>.
  664. /// This is also known as the size or length of the array.
  665. /// </summary>
  666. /// <returns>The number of elements.</returns>
  667. public int Count => _underlyingArray.Count;
  668. /// <summary>
  669. /// Returns <see langword="true"/> if the array is read-only.
  670. /// See <see cref="MakeReadOnly"/>.
  671. /// </summary>
  672. public bool IsReadOnly => _underlyingArray.IsReadOnly;
  673. /// <summary>
  674. /// Makes the <see cref="Array{T}"/> read-only, i.e. disabled modying of the
  675. /// array's elements. Does not apply to nested content, e.g. content of
  676. /// nested arrays.
  677. /// </summary>
  678. public void MakeReadOnly()
  679. {
  680. _underlyingArray.MakeReadOnly();
  681. }
  682. /// <summary>
  683. /// Adds an item to the end of this <see cref="Array{T}"/>.
  684. /// This is the same as <c>append</c> or <c>push_back</c> in GDScript.
  685. /// </summary>
  686. /// <exception cref="InvalidOperationException">
  687. /// The array is read-only.
  688. /// </exception>
  689. /// <param name="item">The item to add.</param>
  690. /// <returns>The new size after adding the item.</returns>
  691. public void Add(T item)
  692. {
  693. ThrowIfReadOnly();
  694. using var variantValue = VariantUtils.CreateFrom(item);
  695. var self = (godot_array)_underlyingArray.NativeValue;
  696. _ = NativeFuncs.godotsharp_array_add(ref self, variantValue);
  697. }
  698. /// <summary>
  699. /// Erases all items from this <see cref="Array{T}"/>.
  700. /// </summary>
  701. /// <exception cref="InvalidOperationException">
  702. /// The array is read-only.
  703. /// </exception>
  704. public void Clear()
  705. {
  706. _underlyingArray.Clear();
  707. }
  708. /// <summary>
  709. /// Checks if this <see cref="Array{T}"/> contains the given item.
  710. /// </summary>
  711. /// <param name="item">The item to look for.</param>
  712. /// <returns>Whether or not this array contains the given item.</returns>
  713. public bool Contains(T item) => IndexOf(item) != -1;
  714. /// <summary>
  715. /// Copies the elements of this <see cref="Array{T}"/> to the given
  716. /// C# array, starting at the given index.
  717. /// </summary>
  718. /// <param name="array">The C# array to copy to.</param>
  719. /// <param name="arrayIndex">The index to start at.</param>
  720. public void CopyTo(T[] array, int arrayIndex)
  721. {
  722. if (array == null)
  723. throw new ArgumentNullException(nameof(array), "Value cannot be null.");
  724. if (arrayIndex < 0)
  725. {
  726. throw new ArgumentOutOfRangeException(nameof(arrayIndex),
  727. "Number was less than the array's lower bound in the first dimension.");
  728. }
  729. int count = Count;
  730. if (array.Length < (arrayIndex + count))
  731. {
  732. throw new ArgumentException(
  733. "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
  734. }
  735. for (int i = 0; i < count; i++)
  736. {
  737. array[arrayIndex] = this[i];
  738. arrayIndex++;
  739. }
  740. }
  741. /// <summary>
  742. /// Removes the first occurrence of the specified value
  743. /// from this <see cref="Array{T}"/>.
  744. /// </summary>
  745. /// <exception cref="InvalidOperationException">
  746. /// The array is read-only.
  747. /// </exception>
  748. /// <param name="item">The value to remove.</param>
  749. /// <returns>A <see langword="bool"/> indicating success or failure.</returns>
  750. public bool Remove(T item)
  751. {
  752. ThrowIfReadOnly();
  753. int index = IndexOf(item);
  754. if (index >= 0)
  755. {
  756. RemoveAt(index);
  757. return true;
  758. }
  759. return false;
  760. }
  761. // IEnumerable<T>
  762. /// <summary>
  763. /// Gets an enumerator for this <see cref="Array{T}"/>.
  764. /// </summary>
  765. /// <returns>An enumerator.</returns>
  766. public IEnumerator<T> GetEnumerator()
  767. {
  768. int count = _underlyingArray.Count;
  769. for (int i = 0; i < count; i++)
  770. {
  771. yield return this[i];
  772. }
  773. }
  774. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
  775. /// <summary>
  776. /// Converts this <see cref="Array{T}"/> to a string.
  777. /// </summary>
  778. /// <returns>A string representation of this array.</returns>
  779. public override string ToString() => _underlyingArray.ToString();
  780. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  781. public static implicit operator Variant(Array<T> from) => Variant.CreateFrom(from);
  782. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  783. public static explicit operator Array<T>(Variant from) => from.AsGodotArray<T>();
  784. private void ThrowIfReadOnly()
  785. {
  786. if (IsReadOnly)
  787. {
  788. throw new InvalidOperationException("Array instance is read-only.");
  789. }
  790. }
  791. }
  792. }