< Summary

Line coverage
99%
Covered lines: 483
Uncovered lines: 3
Coverable lines: 486
Total lines: 1494
Line coverage: 99.3%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
File 1: GetViewHashCode()0%220100%
File 2: ScriptableList()0%110100%
File 2: get_Item(...)0%110100%
File 2: set_Item(...)0%11090%
File 2: get_IsSynchronized()0%110100%
File 2: get_SyncRoot()0%110100%
File 2: get_IsFixedSize()0%110100%
File 2: Add(...)0%110100%
File 2: Add(...)0%11090.91%
File 2: AddInternal(...)0%110100%
File 2: AddRange(...)0%330100%
File 2: BinarySearch(...)0%110100%
File 2: BinarySearch(...)0%110100%
File 2: Clear()0%220100%
File 2: Contains(...)0%110100%
File 2: Contains(...)0%220100%
File 2: ConvertAll[TOutput](...)0%110100%
File 2: ConvertAll[TOutput](...)0%220100%
File 2: CopyTo()0%110100%
File 2: CopyTo(...)0%110100%
File 2: CopyTo(...)0%110100%
File 2: EnsureCapacity(...)0%220100%
File 2: Exists(...)0%110100%
File 2: Find(...)0%110100%
File 2: FindAll(...)0%110100%
File 2: FindAll(...)0%330100%
File 2: FindIndex(...)0%110100%
File 2: FindIndex(...)0%110100%
File 2: FindIndex(...)0%110100%
File 2: FindLast(...)0%110100%
File 2: FindLastIndex(...)0%110100%
File 2: FindLastIndex(...)0%110100%
File 2: FindLastIndex(...)0%110100%
File 2: ForEach(...)0%110100%
File 2: GetEnumerator()0%110100%
File 2: System.Collections.Generic.IEnumerable<T>.GetEnumerator()0%110100%
File 2: GetEnumerator()0%110100%
File 2: GetRange(...)0%110100%
File 2: GetRange(...)0%220100%
File 2: IndexOf(...)0%110100%
File 2: IndexOf(...)0%110100%
File 2: IndexOf(...)0%110100%
File 2: IndexOf(...)0%220100%
File 2: Insert(...)0%110100%
File 2: Insert(...)0%11090%
File 2: InsertInternal(...)0%110100%
File 2: InsertRange(...)0%330100%
File 2: LastIndexOf(...)0%110100%
File 2: LastIndexOf(...)0%110100%
File 2: LastIndexOf(...)0%110100%
File 2: Remove(...)0%110100%
File 2: Remove(...)0%220100%
File 2: RemoveInternal(...)0%220100%
File 2: RemoveAll(...)0%220100%
File 2: RemoveAllWithPredicate(...)0%440100%
File 2: RemoveAt(...)0%110100%
File 2: RemoveRange(...)0%330100%
File 2: Reverse()0%110100%
File 2: Reverse(...)0%110100%
File 2: ReverseInternal(...)0%220100%
File 2: Slice(...)0%110100%
File 2: Slice(...)0%110100%
File 2: SetValue(...)0%330100%
File 2: Sort(...)0%110100%
File 2: Sort(...)0%110100%
File 2: Sort(...)0%110100%
File 2: SortInternal(...)0%330100%
File 2: ToArray()0%110100%
File 2: TrimExcess()0%110100%
File 2: TrueForAll(...)0%110100%
File 2: TryFind(...)0%330100%
File 2: InvokeCollectionChanged()0%330100%
File 2: UpdateCounts()0%110100%
File 2: OnPreStart()0%330100%
File 2: WarnIfLeftOverSubscribers()0%110100%
File 2: WarnIfLeftOverObjects()0%440100%
File 2: ClearSubscribers(...)0%220100%
File 2: OnDisabled()0%110100%

File(s)

./Packages/se.hertzole.scriptable-values/Runtime/Collections/Core/ScriptableList.Bindings.cs

#LineLine coverage
 1#if SCRIPTABLE_VALUES_RUNTIME_BINDING && SCRIPTABLE_VALUES_UITOOLKIT
 2using System.Collections.Generic;
 3
 4namespace Hertzole.ScriptableValues
 5{
 6    partial class ScriptableList<T>
 7    {
 8        /// <inheritdoc />
 9        protected override long GetViewHashCode()
 16010        {
 11            unchecked
 16012            {
 16013                long hash = 17;
 16014                hash = hash * 23 + base.GetViewHashCode();
 16015                hash = hash * 23 + Capacity.GetHashCode();
 16016                hash = hash * 23 + Count.GetHashCode();
 16017                hash = hash * 23 + IsReadOnly.GetHashCode();
 16018                hash = hash * 23 + ClearOnStart.GetHashCode();
 16019                hash = hash * 23 + SetEqualityCheck.GetHashCode();
 20
 16021                EqualityComparer<T> comparer = EqualityComparer<T>.Default;
 22
 461623                for (int i = 0; i < list.Count; i++)
 214824                {
 214825                    hash = hash * 23 + comparer.GetHashCode(list[i]);
 214826                }
 27
 16028                return hash;
 29            }
 16030        }
 31    }
 32}
 33#endif

./Packages/se.hertzole.scriptable-values/Runtime/Collections/Core/ScriptableList.Generic.cs

#LineLine coverage
 1#nullable enable
 2
 3using System;
 4using System.Collections;
 5using System.Collections.Generic;
 6using System.Data;
 7using System.Diagnostics;
 8using System.Runtime.CompilerServices;
 9using Hertzole.ScriptableValues.Helpers;
 10using UnityEngine;
 11using UnityEngine.Assertions;
 12using Debug = UnityEngine.Debug;
 13#if SCRIPTABLE_VALUES_PROPERTIES
 14using Unity.Properties;
 15#endif
 16
 17namespace Hertzole.ScriptableValues
 18{
 19    /// <summary>
 20    ///     A <see cref="ScriptableObject" /> that holds a <see cref="System.Collections.Generic.List{T}" />.
 21    /// </summary>
 22    /// <typeparam name="T">The type of elements in the list.</typeparam>
 23    public abstract partial class ScriptableList<T> : ScriptableList,
 24        IList<T>,
 25        IReadOnlyList<T>,
 26        IList,
 27        INotifyScriptableCollectionChanged<T>
 28    {
 29        [SerializeField]
 30        [EditorTooltip("If read only, the list cannot be changed at runtime and won't be cleared on start.")]
 31#if SCRIPTABLE_VALUES_PROPERTIES
 32        [DontCreateProperty]
 33#endif
 234434        internal bool isReadOnly = false;
 35        [SerializeField]
 36        [EditorTooltip(
 37            "If true, an equality check will be run before setting an item through the indexer to make sure the new obje
 38#if SCRIPTABLE_VALUES_PROPERTIES
 39        [DontCreateProperty]
 40#endif
 234441        internal bool setEqualityCheck = true;
 42        [SerializeField]
 43        [EditorTooltip("If true, the list will be cleared on play mode start/game boot.")]
 44#if SCRIPTABLE_VALUES_PROPERTIES
 45        [DontCreateProperty]
 46#endif
 234447        internal bool clearOnStart = true;
 48        [SerializeField]
 49#if SCRIPTABLE_VALUES_PROPERTIES
 50        [DontCreateProperty]
 51#endif
 234452        internal List<T> list = new List<T>();
 53
 54        private int internalCapacity;
 55        private int internalCount;
 56
 57        /// <inheritdoc />
 58        public sealed override int Capacity
 59        {
 73860            get { return internalCapacity; }
 61            set
 845062            {
 845063                list.Capacity = value;
 845064                SetField(ref internalCapacity, list.Capacity, capacityChangingArgs, capacityChangedArgs);
 845065                Assert.AreEqual(internalCapacity, list.Capacity);
 845066            }
 67        }
 68
 69        /// <inheritdoc cref="ScriptableList.Count" />
 70        public sealed override int Count
 71        {
 9097272            get { return internalCount; }
 73            protected set
 824274            {
 824275                SetField(ref internalCount, value, countChangingArgs, countChangedArgs);
 824276                Assert.AreEqual(internalCount, list.Count);
 824277            }
 78        }
 79
 80        /// <summary>
 81        ///     Gets or sets the element at the specified index.
 82        /// </summary>
 83        public T this[int index]
 84        {
 31168885            get { return list[index]; }
 41486            set { SetValue(index, value); }
 87        }
 88
 89        object? IList.this[int index]
 90        {
 9091            get { return list[index]; }
 92            set
 3093            {
 3094                Guard.ThrowIfNullAndNullsAreIllegal<T>(value, nameof(value));
 95
 96                try
 3097                {
 3098                    this[index] = (T) value!;
 2499                }
 6100                catch (InvalidCastException)
 6101                {
 102                    // The item was not the correct type, throw an exception.
 6103                    ThrowHelper.ThrowWrongExpectedValueType<T>(value);
 0104                }
 24105            }
 106        }
 107
 108        /// <inheritdoc cref="ScriptableList.IsReadOnly" />
 109        public override bool IsReadOnly
 110        {
 30660111            get { return isReadOnly; }
 8238112            set { SetField(ref isReadOnly, value, isReadOnlyChangingArgs, isReadOnlyChangedArgs); }
 113        }
 114
 115        /// <inheritdoc />
 116        public override bool SetEqualityCheck
 117        {
 648118            get { return setEqualityCheck; }
 222119            set { SetField(ref setEqualityCheck, value, setEqualityCheckChangingArgs, setEqualityCheckChangedArgs); }
 120        }
 121
 122        /// <inheritdoc />
 123        public override bool ClearOnStart
 124        {
 648125            get { return clearOnStart; }
 258126            set { SetField(ref clearOnStart, value, clearOnStartChangingArgs, clearOnStartChangedArgs); }
 127        }
 128
 129        // Is this List synchronized (thread-safe)?
 130        bool ICollection.IsSynchronized
 131        {
 18132            get { return false; }
 133        }
 134        // Synchronization root for this object.
 135        object ICollection.SyncRoot
 136        {
 18137            get { return this; }
 138        }
 139
 140        bool IList.IsFixedSize
 141        {
 36142            get { return isReadOnly; }
 143        }
 144
 145#if UNITY_EDITOR
 146        // Used in the CreateAssetMenu attribute.
 147        internal const int ORDER = ScriptableEvent.ORDER + 50;
 148#endif
 149
 150        /// <summary>
 151        ///     Occurs when an item is added, removed, replaced, or the entire <see cref="ScriptableList{T}" /> is refre
 152        /// </summary>
 153        public event CollectionChangedEventHandler<T>? OnCollectionChanged;
 154
 155        /// <summary>
 156        ///     Adds an item to the end of the <see cref="ScriptableList{T}" />.
 157        /// </summary>
 158        /// <param name="item">
 159        ///     The object to be added to the end of the <see cref="ScriptableList{T}" />. The value can be
 160        ///     <c>null</c> for reference types.
 161        /// </param>
 162        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 163        public void Add(T item)
 3774164        {
 3774165            AddInternal(item);
 3750166        }
 167
 168        /// <summary>
 169        ///     Adds an item to the end of the <see cref="ScriptableList{T}" />.
 170        /// </summary>
 171        /// <param name="value">
 172        ///     The object to be added to the end of the <see cref="ScriptableList{T}" />. The value can be
 173        ///     <c>null</c> for reference types.
 174        /// </param>
 175        /// <returns>The position into which the new element was inserted.</returns>
 176        /// <exception cref="ArgumentNullException"><paramref name="value" /> is <c>null</c> and <c>T</c> does not allow
 177        /// <exception cref="ArgumentException"><paramref name="value" /> is of a type that is not assignable to the lis
 178        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 179        int IList.Add(object? value)
 666180        {
 181            // If the item is null and typeof(T) doesn't allow nulls, throw an exception.
 666182            Guard.ThrowIfNullAndNullsAreIllegal<T>(value, nameof(value));
 183
 184            try
 660185            {
 660186                AddInternal((T) value!);
 630187            }
 6188            catch (InvalidCastException)
 6189            {
 190                // The item was not the correct type, throw an exception.
 6191                ThrowHelper.ThrowWrongExpectedValueType<T>(value);
 0192            }
 193
 630194            return Count - 1;
 630195        }
 196
 197        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 198        private void AddInternal(T item)
 4428199        {
 4428200            Guard.IsNotReadOnly(this, name);
 201
 4380202            int index = Count;
 4380203            list.Add(item);
 4380204            UpdateCounts();
 4380205            InvokeCollectionChanged(CollectionChangedArgs<T>.Add(item, index));
 206
 4380207            AddStackTrace(1);
 4380208        }
 209
 210        /// <summary>
 211        ///     Adds the elements of the specified collection to the end of the <see cref="ScriptableList{T}" />.
 212        /// </summary>
 213        /// <param name="collection">
 214        ///     The collection whose elements should be added to the end of the
 215        ///     <see cref="ScriptableList{T}" />. The collection itself cannot be <c>null</c>, but it can contain elemen
 216        ///     <c>null</c>, if type <c>T</c> is a reference type.
 217        /// </param>
 218        /// <exception cref="ArgumentNullException"><paramref name="collection" /> is <c>null</c>.</exception>
 219        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 220        public void AddRange(IEnumerable<T> collection)
 1350221        {
 1350222            Guard.IsNotNull(collection, nameof(collection));
 1344223            Guard.IsNotReadOnly(this, name);
 224
 1320225            using (CollectionScope<T> scope = new CollectionScope<T>(collection))
 1320226            {
 1320227                if (scope.Length == 0)
 30228                {
 30229                    return;
 230                }
 231
 1290232                int index = list.Count;
 233
 1290234                using CollectionEnumerator<T> enumerator = scope.GetEnumerator();
 1290235                list.AddRange(enumerator);
 1290236                UpdateCounts();
 237
 1290238                CollectionChangedArgs<T> args = CollectionChangedArgs<T>.Add(scope.Span, index);
 1290239                InvokeCollectionChanged(in args);
 1290240            }
 241
 1290242            AddStackTrace();
 1320243        }
 244
 245        /// <summary>
 246        ///     Searches the entire sorted <see cref="ScriptableList{T}" /> for an element using the default comparer an
 247        ///     the zero-based index of the element.
 248        /// </summary>
 249        /// <param name="item">The object to locate. The value can be <c>null</c> for reference types.</param>
 250        /// <param name="comparer">
 251        ///     The <see cref="IComparer{T}" /> implementation to use when comparing elements, or <c>null</c> to use the
 252        ///     comparer <see cref="Comparer{T}.Default" />.
 253        /// </param>
 254        /// <returns>
 255        ///     The zero-based index of <paramref name="item" /> in the sorted <see cref="ScriptableList{T}" />, if
 256        ///     <paramref name="item" /> is found;
 257        ///     otherwise, a negative number that is the bitwise complement of the index of the next element that is lar
 258        ///     <paramref name="item" /> or,
 259        ///     if there is no larger element, the bitwise complement of <see cref="Count" />.
 260        /// </returns>
 261        /// <exception cref="InvalidOperationException">
 262        ///     <paramref name="comparer" /> is <c>null</c>, and the default comparer cannot find an implementation of t
 263        ///     <see cref="IComparer{T}" /> generic interface or the <see cref="IComparable" /> interface for type <c>T<
 264        /// </exception>
 265        public int BinarySearch(T item, IComparer<T>? comparer = null)
 1848266        {
 1848267            return BinarySearch(0, internalCount, item, comparer);
 1848268        }
 269
 270        /// <summary>
 271        ///     Searches a range of elements in the sorted <see cref="ScriptableList{T}" /> for an element using the spe
 272        ///     comparer and returns the zero-based index of the element.
 273        /// </summary>
 274        /// <param name="index">The zero-based starting index of the range to search.</param>
 275        /// <param name="count">The length of the range to search.</param>
 276        /// <param name="item">The object to locate. The value can be <c>null</c> for reference types.</param>
 277        /// <param name="comparer">
 278        ///     The <see cref="IComparer{T}" /> implementation to use when comparing elements, or <c>null</c> to use
 279        ///     the default comparer <see cref="Comparer{T}.Default" />.
 280        /// </param>
 281        /// <returns>
 282        ///     The zero-based index of <paramref name="item" /> in the sorted <see cref="ScriptableList{T}" />, if
 283        ///     <paramref name="item" /> is found;
 284        ///     otherwise, a negative number that is the bitwise complement of the index of the next element that is lar
 285        ///     <paramref name="item" /> or,
 286        ///     if there is no larger element, the bitwise complement of <paramref name="index" /> plus <paramref name="
 287        /// </returns>
 288        /// <exception cref="ArgumentOutOfRangeException">
 289        ///     <paramref name="index" /> is less than 0. -or- <paramref name="count" />
 290        ///     is less than 0.
 291        /// </exception>
 292        /// <exception cref="ArgumentException">
 293        ///     <paramref name="index" /> and <paramref name="count" /> do not denote a valid range
 294        ///     in the <see cref="ScriptableList{T}" />.
 295        /// </exception>
 296        /// <exception cref="InvalidOperationException">
 297        ///     <paramref name="comparer" /> is <c>null</c>, and the default comparer cannot find an
 298        ///     implementation of the <see cref="IComparer{T}" /> generic interface or the <see cref="IComparable" /> in
 299        ///     type <typeparamref name="T" />.
 300        /// </exception>
 301        public int BinarySearch(int index, int count, T item, IComparer<T>? comparer = null)
 1920302        {
 1920303            return list.BinarySearch(index, count, item, comparer);
 1848304        }
 305
 306        /// <inheritdoc cref="ScriptableList.Clear" />
 307        public sealed override void Clear()
 2282308        {
 309            // If the game is playing, we don't want to set the value if it's read only.
 2282310            Guard.IsNotReadOnly(this, name);
 311
 2258312            if (list.Count == 0)
 584313            {
 584314                return;
 315            }
 316
 1674317            using CollectionScope<T> scope = new CollectionScope<T>(list);
 318
 1674319            list.Clear();
 1674320            UpdateCounts();
 1674321            InvokeCollectionChanged(CollectionChangedArgs<T>.Clear(scope.Span));
 322
 1674323            AddStackTrace();
 3932324        }
 325
 326        /// <summary>
 327        ///     Determines whether an element is in the <see cref="ScriptableList{T}" />.
 328        /// </summary>
 329        /// <param name="item">
 330        ///     The object to locate in the <see cref="ScriptableList{T}" />. The value can be <c>null</c> for
 331        ///     reference types.
 332        /// </param>
 333        /// <returns>
 334        ///     <c>true</c> if <paramref name="item" /> is found in the <see cref="ScriptableList{T}" />; otherwise,
 335        ///     <c>false</c>.
 336        /// </returns>
 337        public bool Contains(T item)
 30338        {
 30339            return list.Contains(item);
 30340        }
 341
 342        /// <summary>
 343        ///     Determines whether an element is in the <see cref="ScriptableList{T}" />.
 344        /// </summary>
 345        /// <param name="value">
 346        ///     The object to locate in the <see cref="ScriptableList{T}" />. The value can be <c>null</c> for
 347        ///     reference types.
 348        /// </param>
 349        /// <returns><c>true</c> if the item is found in the <see cref="ScriptableList{T}" />; otherwise, <c>false</c>.<
 350        bool IList.Contains(object? value)
 12351        {
 352            // Check if the value is the same type as the generic type and then call the Contains method.
 12353            return EqualityHelper.IsSameType(value, out T? newValue) && Contains(newValue);
 12354        }
 355
 356        /// <summary>
 357        ///     Converts the elements in the current <see cref="ScriptableList{T}" /> to another type, and returns a lis
 358        ///     containing the converted elements.
 359        /// </summary>
 360        /// <param name="converter">
 361        ///     A <see cref="Converter{TInput,TOutput}" /> delegate that converts each element from one type to another 
 362        /// </param>
 363        /// <typeparam name="TOutput">The type of the elements of the target array.</typeparam>
 364        /// <returns>
 365        ///     A <see cref="List{TOutput}" /> of the target type containing the converted elements from the current
 366        ///     <see cref="ScriptableList{T}" />.
 367        /// </returns>
 368        /// <exception cref="ArgumentNullException"><paramref name="converter" /> is <c>null</c>.</exception>
 369        public List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter)
 12370        {
 12371            Guard.IsNotNull(converter, nameof(converter));
 372
 6373            return list.ConvertAll(converter);
 6374        }
 375
 376        /// <summary>
 377        ///     Converts the elements in the current list to another type, and copies the converted elements to the spec
 378        /// </summary>
 379        /// <param name="destinationList">The destination list where the converted items will be copied to.</param>
 380        /// <param name="converter">
 381        ///     A <see cref="Converter{TInput,TOutput}" /> delegate that converts each element from one type to another 
 382        /// </param>
 383        /// <typeparam name="TOutput">The type of the elements of the target array.</typeparam>
 384        /// <exception cref="ArgumentNullException"><c>converter</c> is <c>null</c>.</exception>
 385        /// <exception cref="ReadOnlyException"><paramref name="destinationList" /> is marked as read-only.</exception>
 386        public void ConvertAll<TOutput>(ICollection<TOutput> destinationList, Converter<T, TOutput> converter)
 30387        {
 30388            Guard.IsNotNull(destinationList, nameof(destinationList));
 24389            Guard.IsNotNull(converter, nameof(converter));
 18390            Guard.IsNotReadOnly(destinationList, nameof(destinationList));
 391
 12392            destinationList.Clear();
 393
 394            // Make sure the capacity is at least the same as the internal count.
 12395            destinationList.TryEnsureCapacity(internalCount);
 396
 144397            for (int i = 0; i < internalCount; i++)
 60398            {
 60399                destinationList.Add(converter(list[i]));
 60400            }
 12401        }
 402
 403        /// <summary>
 404        ///     Copies the entire <see cref="ScriptableList{T}" /> to a compatible one-dimensional array, starting at th
 405        ///     of the target array.
 406        /// </summary>
 407        /// <param name="array">
 408        ///     The one-dimensional array that is the destination of the elements copied from
 409        ///     <see cref="ScriptableList{T}" />. The array must have zero-based indexing.
 410        /// </param>
 411        /// <exception cref="ArgumentNullException"><paramref name="array" /> is <c>null</c>.</exception>
 412        /// <exception cref="ArgumentException">
 413        ///     The number of elements in the source <see cref="ScriptableList{T}" /> is greater
 414        ///     than the number of elements that the destination array can contain.
 415        /// </exception>
 416        public void CopyTo(T[] array)
 6417        {
 6418            list.CopyTo(array);
 6419        }
 420
 421        /// <summary>
 422        ///     Copies the entire list to a compatible one-dimensional array, starting at the specified index of the tar
 423        /// </summary>
 424        /// <param name="array">The array destination.</param>
 425        /// <param name="arrayIndex">The index in the destination array where the copying begins.</param>
 426        public void CopyTo(T[] array, int arrayIndex)
 48427        {
 48428            list.CopyTo(array, arrayIndex);
 48429        }
 430
 431        /// <summary>
 432        ///     Copies the entire list to a compatible one-dimensional array, starting at the specified index of the tar
 433        /// </summary>
 434        /// <param name="array">The array destination.</param>
 435        /// <param name="index">The index in the destination array where the copying begins.</param>
 436        void ICollection.CopyTo(Array array, int index)
 6437        {
 6438            ((ICollection) list).CopyTo(array, index);
 6439        }
 440
 441        /// <inheritdoc />
 442        public sealed override void EnsureCapacity(int capacity)
 180443        {
 180444            if (list.Capacity < capacity)
 162445            {
 162446                Capacity = capacity;
 162447            }
 180448        }
 449
 450        /// <summary>
 451        ///     Determines whether the <see cref="ScriptableList{T}" /> contains elements that match the conditions defi
 452        ///     specified predicate.
 453        /// </summary>
 454        /// <param name="match">The <see cref="Predicate{T}" /> delegate that defines the conditions of the elements to 
 455        /// <returns>
 456        ///     <c>true</c> if the <see cref="ScriptableList{T}" /> contains one or more elements that match the conditi
 457        ///     by the specified predicate; otherwise, <c>false</c>.
 458        /// </returns>
 459        /// <exception cref="ArgumentNullException"><paramref name="match" /> is <c>null</c>.</exception>
 460        public bool Exists(Predicate<T> match)
 12461        {
 12462            return list.Exists(match);
 12463        }
 464
 465        /// <summary>
 466        ///     Searches for an element that matches the conditions defined by the specified predicate, and returns the 
 467        ///     occurrence within the entire <see cref="ScriptableList{T}" />.
 468        /// </summary>
 469        /// <param name="match">The <see cref="Predicate{T}" /> delegate that defines the conditions of the element to s
 470        /// <returns>
 471        ///     The first element that matches the conditions defined by the specified predicate, if found; otherwise, t
 472        ///     value for type <typeparamref name="T" />.
 473        /// </returns>
 474        /// <exception cref="ArgumentNullException"><paramref name="match" /> is <c>null</c>.</exception>
 475        public T? Find(Predicate<T> match)
 12476        {
 12477            return list.Find(match);
 12478        }
 479
 480        /// <summary>
 481        ///     Retrieves all the elements that match the conditions defined by the specified predicate.
 482        /// </summary>
 483        /// <param name="match">The <see cref="Predicate{T}" /> delegate that defines the conditions of the elements to 
 484        /// <returns>
 485        ///     A <see cref="List{T}" /> containing all the elements that match the conditions defined by the specified 
 486        ///     if found; otherwise, an empty <see cref="List{T}" />
 487        /// </returns>
 488        /// <exception cref="ArgumentNullException"><c>match</c> is <c>null</c>.</exception>
 489        public List<T> FindAll(Predicate<T> match)
 18490        {
 18491            return list.FindAll(match);
 12492        }
 493
 494        /// <summary>
 495        ///     Retrieves all the elements that match the conditions defined by the specified predicate and copies them 
 496        ///     specified list.
 497        /// </summary>
 498        /// <param name="destinationList"> The list where the found elements will be copied to.</param>
 499        /// <param name="match">The <see cref="Predicate{T}" /> delegate that defines the conditions of the elements to 
 500        /// <exception cref="ArgumentNullException"><c>match</c> is <c>null</c>.</exception>
 501        /// <exception cref="ArgumentException"><paramref name="destinationList" /> is marked as read-only.</exception>
 502        public void FindAll(IList<T> destinationList, Predicate<T> match)
 42503        {
 42504            Guard.IsNotNull(match, nameof(match));
 36505            Guard.IsNotNull(destinationList, nameof(destinationList));
 30506            Guard.IsNotReadOnly(destinationList, nameof(destinationList));
 507
 24508            destinationList.Clear();
 509
 360510            for (int i = 0; i < internalCount; i++)
 156511            {
 156512                if (match(list[i]))
 108513                {
 108514                    destinationList.Add(list[i]);
 108515                }
 156516            }
 24517        }
 518
 519        /// <summary>
 520        ///     Searches for an element that matches the conditions defined by the specified predicate, and returns the 
 521        ///     index of the first occurrence within the entire list.
 522        /// </summary>
 523        /// <param name="match">The <see cref="Predicate{T}" /> delegate that defines the conditions of the element to s
 524        /// <returns>
 525        ///     The zero-based index of the first occurrence of an element that matches the conditions defined by <c>mat
 526        ///     found; otherwise, -1.
 527        /// </returns>
 528        /// <exception cref="ArgumentNullException"><c>match</c> is <c>null</c>.</exception>
 529        public int FindIndex(Predicate<T> match)
 12530        {
 12531            return list.FindIndex(match);
 6532        }
 533
 534        /// <summary>
 535        ///     Searches for an element that matches the conditions defined by the specified predicate, and returns the 
 536        ///     index of the first occurrence within the range of elements in the list that extends from the specified i
 537        ///     last element.
 538        /// </summary>
 539        /// <param name="startIndex">The zero-based starting index of the search.</param>
 540        /// <param name="match">The <see cref="Predicate{T}" /> delegate that defines the conditions of the element to s
 541        /// <returns>
 542        ///     The zero-based index of the first occurrence of an element that matches the conditions defined by <c>mat
 543        ///     found; otherwise, -1.
 544        /// </returns>
 545        /// <exception cref="ArgumentNullException"><c>match</c> is <c>null</c>.</exception>
 546        /// <exception cref="ArgumentOutOfRangeException"><c>startIndex</c> is outside the range of valid indexes for th
 547        public int FindIndex(int startIndex, Predicate<T> match)
 24548        {
 24549            return list.FindIndex(startIndex, match);
 6550        }
 551
 552        /// <summary>
 553        ///     Searches for an element that matches the conditions defined by the specified predicate, and returns the 
 554        ///     index of the first occurrence within the range of elements in the list that starts at the specified inde
 555        ///     contains the specified number of elements.
 556        /// </summary>
 557        /// <param name="startIndex">The zero-based starting index of the search.</param>
 558        /// <param name="count">The number of elements in the section to search.</param>
 559        /// <param name="match">The <see cref="Predicate{T}" /> delegate that defines the conditions of the element to s
 560        /// <returns>
 561        ///     The zero-based index of the first occurrence of an element that matches the conditions defined by <c>mat
 562        ///     found; otherwise, -1.
 563        /// </returns>
 564        /// <exception cref="ArgumentNullException"><c>match</c> is <c>null</c>.</exception>
 565        /// <exception cref="ArgumentOutOfRangeException">
 566        ///     <c>startIndex</c> is outside the range of valid indexes for the list. Or
 567        ///     <c>count</c> is less than 0. Or <c>startIndex</c> and <c>count</c> do not specify a valid section in the
 568        /// </exception>
 569        public int FindIndex(int startIndex, int count, Predicate<T> match)
 30570        {
 30571            return list.FindIndex(startIndex, count, match);
 6572        }
 573
 574        /// <summary>
 575        ///     Searches for an element that matches the conditions defined by the specified predicate, and returns the 
 576        ///     occurrence within the entire list.
 577        /// </summary>
 578        /// <param name="match">The <see cref="Predicate{T}" /> delegate that defines the conditions of the element to s
 579        /// <returns>
 580        ///     The last element that matches the conditions defined by the specified predicate, if found; otherwise, th
 581        ///     value for type <c>T</c>.
 582        /// </returns>
 583        /// <exception cref="ArgumentNullException"><c>match</c> is <c>null</c>.</exception>
 584        public T? FindLast(Predicate<T> match)
 12585        {
 12586            return list.FindLast(match);
 6587        }
 588
 589        /// <summary>
 590        ///     Searches for an element that matches the conditions defined by the specified predicate, and returns the 
 591        ///     index of the last occurrence within the entire list.
 592        /// </summary>
 593        /// <param name="match">The <see cref="Predicate{T}" /> delegate that defines the conditions of the element to s
 594        /// <returns>
 595        ///     The zero-based index of the last occurrence of an element that matches the conditions defined by <c>matc
 596        ///     if found; otherwise, -1.
 597        /// </returns>
 598        /// <exception cref="ArgumentNullException"><c>match</c> is <c>null</c>.</exception>
 599        public int FindLastIndex(Predicate<T> match)
 12600        {
 12601            return list.FindLastIndex(match);
 6602        }
 603
 604        /// <summary>
 605        ///     Searches for an element that matches the conditions defined by the specified predicate, and returns the 
 606        ///     index of the last occurrence within the range of elements in the list that extends from the first elemen
 607        ///     specified index.
 608        /// </summary>
 609        /// <param name="startIndex">The zero-based starting index of the backward search.</param>
 610        /// <param name="match">The <see cref="Predicate{T}" /> delegate that defines the conditions of the element to s
 611        /// <returns>
 612        ///     The zero-based index of the last occurrence of an element that matches the conditions defined by <c>matc
 613        ///     if found; otherwise, -1.
 614        /// </returns>
 615        /// <exception cref="ArgumentNullException"><c>match</c> is <c>null</c>.</exception>
 616        /// <exception cref="ArgumentOutOfRangeException"><c>startIndex</c> is outside the range of valid indexes for th
 617        public int FindLastIndex(int startIndex, Predicate<T> match)
 24618        {
 24619            return list.FindLastIndex(startIndex, match);
 6620        }
 621
 622        /// <summary>
 623        ///     Searches for an element that matches the conditions defined by the specified predicate, and returns the 
 624        ///     index of the last occurrence within the range of elements in the list that contains the specified number
 625        ///     elements and ends at the specified index.
 626        /// </summary>
 627        /// <param name="startIndex">The zero-based starting index of the backward search.</param>
 628        /// <param name="count">The number of elements in the section to search.</param>
 629        /// <param name="match">The <see cref="Predicate{T}" /> delegate that defines the conditions of the element to s
 630        /// <returns>
 631        ///     The zero-based index of the last occurrence of an element that matches the conditions defined by <c>matc
 632        ///     if found; otherwise, -1.
 633        /// </returns>
 634        /// <exception cref="ArgumentNullException"><c>match</c> is <c>null</c>.</exception>
 635        /// <exception cref="ArgumentOutOfRangeException">
 636        ///     <c>startIndex</c> is outside the range of valid indexes for the list. Or
 637        ///     <c>count</c> is less than 0. Or <c>startIndex</c> and <c>count</c> do not specify a valid section in the
 638        /// </exception>
 639        public int FindLastIndex(int startIndex, int count, Predicate<T> match)
 30640        {
 30641            return list.FindLastIndex(startIndex, count, match);
 6642        }
 643
 644        /// <summary>
 645        ///     Performs the specified action on each element of the list.
 646        /// </summary>
 647        /// <param name="action">The <see cref="Action{T}" /> delegate to perform on each element of the list.</param>
 648        /// <exception cref="ArgumentNullException"><c>action</c> is <c>null</c>.</exception>
 649        /// <exception cref="InvalidOperationException">An element in the collection has been modified.</exception>
 650        public void ForEach(Action<T> action)
 18651        {
 18652            list.ForEach(action);
 6653        }
 654
 655        /// <summary>
 656        ///     Returns an enumerator that iterates through the list.
 657        /// </summary>
 658        /// <returns>A <see cref="List{T}.Enumerator" /> for the list.</returns>
 659        public List<T>.Enumerator GetEnumerator()
 36660        {
 36661            return list.GetEnumerator();
 36662        }
 663
 664        /// <summary>
 665        ///     Returns an enumerator that iterates through the list.
 666        /// </summary>
 667        /// <returns>A <see cref="List{T}.Enumerator" /> for the list.</returns>
 668        IEnumerator<T> IEnumerable<T>.GetEnumerator()
 468669        {
 468670            return ((IEnumerable<T>) list).GetEnumerator();
 468671        }
 672
 673        /// <summary>
 674        ///     Returns an enumerator that iterates through the list.
 675        /// </summary>
 676        /// <returns>A <see cref="List{T}.Enumerator" />  for the list.</returns>
 677        IEnumerator IEnumerable.GetEnumerator()
 12678        {
 12679            return ((IEnumerable) list).GetEnumerator();
 12680        }
 681
 682        /// <summary>
 683        ///     Creates a shallow copy of a range of elements in the source list.
 684        /// </summary>
 685        /// <param name="index">The zero-based list index at which the range starts.</param>
 686        /// <param name="count">The number of elements in the range.</param>
 687        /// <returns>A shallow copy of a range of elements in the source list.</returns>
 688        /// <exception cref="ArgumentOutOfRangeException"><c>index</c> is less than 0. Or <c>count</c> is less than 0.</
 689        /// <exception cref="ArgumentException"><c>index</c> and <c>count</c> do not denote a valid range of elements in
 690        public List<T> GetRange(int index, int count)
 84691        {
 84692            return list.GetRange(index, count);
 12693        }
 694
 695        /// <summary>
 696        ///     Copies a range of elements from the source list to the specified destination list.
 697        /// </summary>
 698        /// <param name="index">The zero-based list index at which the range starts.</param>
 699        /// <param name="count">The number of elements in the range.</param>
 700        /// <param name="destinationList">The list where the copied elements will be stored.</param>
 701        /// <exception cref="ArgumentNullException"><c>destinationList</c> is <c>null</c>.</exception>
 702        /// <exception cref="ArgumentOutOfRangeException"><c>index</c> is less than 0. Or <c>count</c> is less than 0.</
 703        /// <exception cref="ArgumentException"><c>index</c> and <c>count</c> do not denote a valid range of elements in
 704        /// <exception cref="ArgumentException"><paramref name="destinationList" /> is marked as read-only.</exception>
 705        public void GetRange(int index, int count, IList<T> destinationList)
 204706        {
 204707            Guard.IsNotNull(destinationList, nameof(destinationList));
 180708            Guard.IsNotReadOnly(destinationList, nameof(destinationList));
 168709            Guard.IsGreaterThanOrEqualTo(index, 0, nameof(index));
 144710            Guard.IsGreaterThanOrEqualTo(count, 0, nameof(count));
 120711            Guard.IsGreaterThanOrEqualTo(internalCount - index, count, nameof(Count) + " - " + nameof(index));
 712
 24713            destinationList.Clear();
 714
 715            // Make sure the capacity is at least the same as the internal count.
 24716            destinationList.TryEnsureCapacity(count);
 717
 288718            for (int i = index; i < index + count; i++)
 120719            {
 120720                destinationList.Add(list[i]);
 120721            }
 24722        }
 723
 724        /// <summary>
 725        ///     Searches for the specified object and returns the zero-based index of the first occurrence within the en
 726        ///     Returns -1 if the item is not found.
 727        /// </summary>
 728        /// <param name="item">The object to locate in the list.</param>
 729        /// <returns>The zero-based index of the first occurrence of item within the entire list, if found; otherwise, -
 730        public int IndexOf(T item)
 24731        {
 24732            return list.IndexOf(item);
 24733        }
 734
 735        /// <summary>
 736        ///     Searches for the specified object and returns the zero-based index of the first occurrence within the ra
 737        ///     elements in the list that extends from the specified index to the last element.
 738        /// </summary>
 739        /// <param name="item">The object to locate in the list.</param>
 740        /// <param name="index">The zero-based starting index of the search. 0 (zero) is valid in an empty list.</param>
 741        /// <returns>
 742        ///     The zero-based index of the first occurrence of <c>item</c> within the range of elements in the list tha
 743        ///     extends from <c>index</c> to the last element, if found; otherwise, -1.
 744        /// </returns>
 745        /// <exception cref="ArgumentOutOfRangeException"><c>index</c> is outside the range of valid indexes for the lis
 746        public int IndexOf(T item, int index)
 18747        {
 18748            return list.IndexOf(item, index);
 6749        }
 750
 751        /// <summary>
 752        ///     Searches for the specified object and returns the zero-based index of the first occurrence within the ra
 753        ///     elements in the list that starts at the specified index and contains the specified number of elements.
 754        /// </summary>
 755        /// <param name="item">The object to locate in the list.</param>
 756        /// <param name="index">The zero-based starting index of the search. 0 (zero) is valid in an empty list.</param>
 757        /// <param name="count">The number of elements in the section to search.</param>
 758        /// <returns>
 759        ///     The zero-based index of the first occurrence of <c>item</c> within the range of elements in the list tha
 760        ///     extends from <c>index</c> to the last element, if found; otherwise, -1.
 761        /// </returns>
 762        /// <exception cref="ArgumentOutOfRangeException">
 763        ///     <c>index</c> is outside the range of valid indexes for the list. Or
 764        ///     <c>count</c> is less than 0. Or <c>index</c> and <c>count</c> do not specify a valid section in the list
 765        /// </exception>
 766        public int IndexOf(T item, int index, int count)
 30767        {
 30768            return list.IndexOf(item, index, count);
 6769        }
 770
 771        /// <summary>
 772        ///     Searches for the specified object and returns the zero-based index of the first occurrence within the en
 773        ///     Returns -1 if the item is not found.
 774        /// </summary>
 775        /// <param name="value">The object to locate in the list.</param>
 776        /// <returns>The zero-based index of the first occurrence of item within the entire list, if found; otherwise, -
 777        int IList.IndexOf(object value)
 18778        {
 779            // Check if the value is the same type as the generic type.
 18780            if (EqualityHelper.IsSameType(value, out T? newValue))
 12781            {
 12782                return IndexOf(newValue);
 783            }
 784
 785            // It was not a valid type, return -1.
 6786            return -1;
 18787        }
 788
 789        /// <summary>
 790        ///     Inserts an element into the list at the specified index.
 791        /// </summary>
 792        /// <param name="index">The index where the item should be inserted.</param>
 793        /// <param name="item">The object to insert.</param>
 794        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 795        public void Insert(int index, T item)
 690796        {
 690797            InsertInternal(index, item);
 648798        }
 799
 800        /// <summary>
 801        ///     Inserts an element into the list at the specified index.
 802        /// </summary>
 803        /// <param name="index">The zero-based index at which item should be inserted.</param>
 804        /// <param name="value">The item to insert.</param>
 805        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 806        void IList.Insert(int index, object? value)
 60807        {
 60808            Guard.ThrowIfNullAndNullsAreIllegal<T>(value, nameof(value));
 809
 810            try
 54811            {
 54812                InsertInternal(index, (T) value!);
 24813            }
 6814            catch (InvalidCastException)
 6815            {
 6816                ThrowHelper.ThrowWrongExpectedValueType<T>(value);
 0817            }
 24818        }
 819
 820        /// <summary>
 821        ///     Internal method to insert an item into the list. Skips one frame in the stack traces to give the impress
 822        ///     the calling method inserted the item.
 823        /// </summary>
 824        /// <param name="index">The index where the item should be inserted.</param>
 825        /// <param name="item">The item to insert.</param>
 826        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 827        /// <exception cref="ArgumentOutOfRangeException">If the index is out of bounds.</exception>
 828        private void InsertInternal(int index, T item)
 738829        {
 830            // If the game is playing, we don't want to set the value if it's read only.
 738831            Guard.IsNotReadOnly(this, name);
 696832            Guard.IsInRange(index, 0, list.Count, nameof(index));
 833
 672834            list.Insert(index, item);
 672835            UpdateCounts();
 672836            InvokeCollectionChanged(CollectionChangedArgs<T>.Add(item, index));
 837
 672838            AddStackTrace(1);
 672839        }
 840
 841        /// <summary>
 842        ///     Inserts the elements of a collection into the list at the specified index.
 843        /// </summary>
 844        /// <param name="index">The zero-based index at which the new elements should be inserted.</param>
 845        /// <param name="collection">The collection whose elements should be inserted into the list.</param>
 846        /// <exception cref="ArgumentNullException">If <paramref name="collection" /> is null.</exception>
 847        /// <exception cref="ArgumentOutOfRangeException">
 848        ///     If <paramref name="index" /> is less than 0 or is greater than
 849        ///     <see cref="Count" />.
 850        /// </exception>
 851        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 852        public void InsertRange(int index, IEnumerable<T> collection)
 84853        {
 84854            Guard.IsNotNull(collection, nameof(collection));
 78855            Guard.IsInRange(index, 0, list.Count, nameof(index));
 72856            Guard.IsNotReadOnly(this, name);
 857
 42858            using (CollectionScope<T> scope = new CollectionScope<T>(collection))
 42859            {
 42860                if (scope.Length == 0)
 18861                {
 18862                    return;
 863                }
 864
 24865                using CollectionEnumerator<T> enumerator = scope.GetEnumerator();
 24866                list.InsertRange(index, enumerator);
 24867                UpdateCounts();
 24868                CollectionChangedArgs<T> args = CollectionChangedArgs<T>.Add(scope.Span, index);
 24869                InvokeCollectionChanged(args);
 24870            }
 871
 24872            AddStackTrace();
 42873        }
 874
 875        /// <summary>
 876        ///     Searches for the specified object and returns the zero-based index of the last occurrence within the ent
 877        /// </summary>
 878        /// <param name="item">The object to locate in the list.</param>
 879        /// <returns>
 880        ///     The zero-based index of the last occurrence of <c>item</c> within the entire the list, if found; otherwi
 881        /// </returns>
 882        public int LastIndexOf(T item)
 12883        {
 12884            return list.LastIndexOf(item);
 12885        }
 886
 887        /// <summary>
 888        ///     Searches for the specified object and returns the zero-based index of the last occurrence within the ran
 889        ///     elements in the list that extends from the first element to the specified index.
 890        /// </summary>
 891        /// <param name="item">The object to locate in the list.</param>
 892        /// <param name="index">The zero-based starting index of the backward search.</param>
 893        /// <returns>
 894        ///     The zero-based index of the last occurrence of <c>item</c> within the range of elements in the list that
 895        ///     extends from the first element to <c>index</c>, if found; otherwise, -1.
 896        /// </returns>
 897        /// <exception cref="ArgumentOutOfRangeException"><c>index</c> is outside the range of valid indexes for the lis
 898        public int LastIndexOf(T item, int index)
 18899        {
 18900            return list.LastIndexOf(item, index);
 6901        }
 902
 903        /// <summary>
 904        ///     Searches for the specified object and returns the zero-based index of the last occurrence within the ran
 905        ///     elements in the list that contains the specified number of elements and ends at the specified index.
 906        /// </summary>
 907        /// <param name="item">The object to locate in the list.</param>
 908        /// <param name="index">The zero-based starting index of the backward search.</param>
 909        /// <param name="count">The number of elements in the section to search.</param>
 910        /// <returns>
 911        ///     The zero-based index of the last occurrence of <c>item</c> within the range of elements in the list that
 912        ///     contains <c>count</c> number of elements and ends at <c>index</c>, if found; otherwise, -1.
 913        /// </returns>
 914        /// <exception cref="ArgumentOutOfRangeException">
 915        ///     <c>index</c> is outside the range of valid indexes for the list. Or
 916        ///     <c>count</c> is less than 0. Or <c>index</c> and <c>count</c> do not specify a valid section in the list
 917        /// </exception>
 918        public int LastIndexOf(T item, int index, int count)
 30919        {
 30920            return list.LastIndexOf(item, index, count);
 6921        }
 922
 923        /// <summary>
 924        ///     Removes the first occurrence of a specific object from the <see cref="ScriptableList{T}" />.
 925        /// </summary>
 926        /// <param name="item">
 927        ///     The object to remove from the <see cref="ScriptableList{T}" />. The value can be <c>null</c> for
 928        ///     reference types.
 929        /// </param>
 930        /// <returns>
 931        ///     <c>true</c> if item was successfully removed from the <see cref="ScriptableList{T}" />;
 932        ///     otherwise, <c>false</c>. This method also returns <c>false</c> if item is not found in the original
 933        ///     <see cref="ScriptableList{T}" />.
 934        /// </returns>
 935        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 936        public bool Remove(T item)
 162937        {
 162938            return RemoveInternal(item);
 114939        }
 940
 941        /// <summary>
 942        ///     Removes the first occurrence of a specific object from the list.
 943        /// </summary>
 944        /// <param name="value">The object to remove from the list.</param>
 945        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 946        void IList.Remove(object value)
 66947        {
 948            // Check if the value is the same type as the generic type.
 66949            if (EqualityHelper.IsSameType(value, out T? newValue))
 60950            {
 60951                Remove(newValue);
 36952            }
 42953        }
 954
 955        /// <summary>
 956        ///     Internal method to remove an item from the list. Skips one frame in the stack traces to give the impress
 957        ///     the calling method removed the item.
 958        /// </summary>
 959        /// <param name="item">The item to remove.</param>
 960        /// <returns>True if the item was removed; otherwise false.</returns>
 961        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 962        private bool RemoveInternal(T item)
 162963        {
 964            // If the game is playing, we don't want to set the value if it's read only.
 162965            Guard.IsNotReadOnly(this, name);
 966
 114967            int index = list.IndexOf(item);
 114968            if (index == -1)
 48969            {
 48970                return false;
 971            }
 972
 66973            T? itemToRemove = list[index];
 66974            list.RemoveAt(index);
 66975            UpdateCounts();
 66976            InvokeCollectionChanged(CollectionChangedArgs<T>.Remove(itemToRemove, index));
 977
 66978            AddStackTrace();
 979
 66980            return true;
 114981        }
 982
 983        /// <summary>
 984        ///     Removes all the elements that match the conditions defined by the specified predicate.
 985        /// </summary>
 986        /// <param name="match">The <see cref="Predicate{T}" /> delegate that defines the conditions of the elements to 
 987        /// <returns>The number of elements removed from the <see cref="ScriptableList{T}" />.</returns>
 988        /// <exception cref="ArgumentNullException"><paramref name="match" /> is <c>null</c>.</exception>
 989        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 990        public int RemoveAll(Predicate<T> match)
 84991        {
 84992            Guard.IsNotNull(match, nameof(match));
 993
 994            // If the game is playing, we don't want to set the value if it's read only.
 78995            Guard.IsNotReadOnly(this, name);
 996
 54997            using SpanOwner<T> removed = SpanOwner<T>.Allocate(list.Count);
 54998            int removeLength = RemoveAllWithPredicate(removed.Span, out int firstIndex, match);
 999
 541000            if (removeLength > 0)
 301001            {
 301002                int removeCount = list.RemoveAll(match);
 301003                InvokeCollectionChanged(CollectionChangedArgs<T>.Remove(removed.Span.Slice(0, removeLength), firstIndex)
 1004
 301005                Assert.AreEqual(removeLength, removeCount, "The expected count of removed items is not the same as the a
 1006
 301007                UpdateCounts();
 301008                return removeCount;
 1009            }
 1010
 241011            return 0;
 541012        }
 1013
 1014        private int RemoveAllWithPredicate(Span<T> removedBuffer, out int firstIndex, Predicate<T> match)
 541015        {
 1016            // Keeps track of the number of items removed.
 541017            int removeLength = 0;
 1018            // Keeps track of the first index of the item that was removed.
 541019            firstIndex = -1;
 10801020            for (int i = 0; i < list.Count; i++)
 4861021            {
 4861022                if (match(list[i]))
 2701023                {
 1024                    // If the item was removed, and we haven't set the first index yet, set it.
 2701025                    if (firstIndex == -1)
 301026                    {
 301027                        firstIndex = i;
 301028                    }
 1029
 2701030                    removedBuffer[removeLength] = list[i];
 2701031                    removeLength++;
 2701032                }
 4861033            }
 1034
 541035            return removeLength;
 541036        }
 1037
 1038        /// <summary>
 1039        ///     Removes the element at the specified index of the <see cref="ScriptableList{T}" />.
 1040        /// </summary>
 1041        /// <param name="index">The zero-based index of the element to remove.</param>
 1042        /// <exception cref="ArgumentOutOfRangeException">
 1043        ///     <paramref name="index" /> is less than 0.
 1044        ///     -or-
 1045        ///     <paramref name="index" /> is equal to or greater than <see cref="Count" />.
 1046        /// </exception>
 1047        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 1048        public void RemoveAt(int index)
 841049        {
 1050            // If the game is playing, we don't want to set the value if it's read only.
 841051            Guard.IsNotReadOnly(this, name);
 1052
 601053            T? removedItem = list[index];
 481054            list.RemoveAt(index);
 481055            UpdateCounts();
 481056            InvokeCollectionChanged(CollectionChangedArgs<T>.Remove(removedItem, index));
 1057
 481058            AddStackTrace();
 481059        }
 1060
 1061        /// <summary>
 1062        ///     Removes a range of elements from the list.
 1063        /// </summary>
 1064        /// <param name="index">The zero-based starting index of the range of elements to remove.</param>
 1065        /// <param name="count">The number of elements to remove.</param>
 1066        /// <exception cref="ArgumentOutOfRangeException">
 1067        ///     If <paramref name="index" /> is less than 0 or greater than
 1068        ///     <see cref="Count" />.
 1069        /// </exception>
 1070        /// <exception cref="ArgumentOutOfRangeException">
 1071        ///     If <paramref name="index" /> is less than 0 or greater than
 1072        ///     <see cref="Count" />.
 1073        /// </exception>
 1074        /// <exception cref="ArgumentOutOfRangeException">If <paramref name="count" /> is less than 0.</exception>
 1075        /// <exception cref="ArgumentException">
 1076        ///     If <paramref name="index" /> and <paramref name="count" /> do not denote a valid
 1077        ///     range of elements.
 1078        /// </exception>
 1079        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 1080        public void RemoveRange(int index, int count)
 721081        {
 721082            Guard.IsInRange(index, 0, list.Count, nameof(index));
 601083            Guard.IsGreaterThanOrEqualTo(count, 0, nameof(count));
 601084            Guard.IsNotReadOnly(this, name);
 1085
 1086            // Calculate how many items to remove based on the count and index.
 361087            int countToRemove = Math.Min(count, list.Count - index);
 1088
 361089            if (countToRemove > 0)
 361090            {
 361091                using SpanOwner<T> removed = SpanOwner<T>.Allocate(countToRemove);
 361092                Span<T> removedSpan = removed.Span;
 1093
 3121094                for (int i = 0; i < countToRemove; i++)
 1201095                {
 1201096                    removedSpan[i] = list[index + i];
 1201097                }
 1098
 361099                list.RemoveRange(index, count);
 1100
 301101                UpdateCounts();
 301102                InvokeCollectionChanged(CollectionChangedArgs<T>.Remove(removedSpan, index));
 301103            }
 1104
 301105            AddStackTrace();
 301106        }
 1107
 1108        /// <inheritdoc />
 1109        public sealed override void Reverse()
 721110        {
 721111            ReverseInternal(0, list.Count);
 481112        }
 1113
 1114        /// <inheritdoc />
 1115        public sealed override void Reverse(int index, int count)
 901116        {
 901117            ReverseInternal(index, count);
 481118        }
 1119
 1120        /// <summary>
 1121        ///     Internal reverse method that skips a frame in the stack traces.
 1122        /// </summary>
 1123        /// <param name="index"></param>
 1124        /// <param name="count"></param>
 1125        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 1126        private void ReverseInternal(int index, int count)
 1621127        {
 1128            // If the game is playing, we don't want to set the value if it's read only.
 1621129            Guard.IsNotReadOnly(this, name);
 1130
 1131            // There are no items in the list, so we don't need to do anything.
 1141132            if (list.Count == 0)
 361133            {
 361134                AddStackTrace(1);
 361135                return;
 1136            }
 1137
 781138            using CollectionScope<T> oldItemsScope = CollectionScope<T>.FromListSlice(list, index, count);
 601139            list.Reverse(index, count);
 1140
 601141            using CollectionScope<T> newItemsScope = CollectionScope<T>.FromListSlice(list, index, count);
 601142            InvokeCollectionChanged(CollectionChangedArgs<T>.Replace(oldItemsScope.Span, newItemsScope.Span, index));
 1143
 601144            AddStackTrace(1);
 1561145        }
 1146
 1147        /// <inheritdoc cref="GetRange(int,int)" />
 1148        public List<T> Slice(int index, int count)
 421149        {
 421150            return GetRange(index, count);
 61151        }
 1152
 1153        /// <inheritdoc cref="GetRange(int,int,IList{T})" />
 1154        public void Slice(int index, int count, IList<T> destinationList)
 1021155        {
 1021156            GetRange(index, count, destinationList);
 121157        }
 1158
 1159        /// <summary>
 1160        ///     Sets the value at the given index.
 1161        /// </summary>
 1162        /// <param name="index">The index where to set the item.</param>
 1163        /// <param name="value">The new value to set.</param>
 1164        private void SetValue(int index, T value)
 1561165        {
 1166            // If the game is playing, we don't want to set the value if it's read only.
 1561167            Guard.IsNotReadOnly(this, name);
 1168
 1169            // If the equality check is enabled, we don't want to set the value if it's the same as the current value.
 1141170            if (setEqualityCheck && EqualityHelper.Equals(list[index], value))
 181171            {
 181172                return;
 1173            }
 1174
 841175            T oldValue = list[index];
 841176            list[index] = value;
 841177            InvokeCollectionChanged(CollectionChangedArgs<T>.Replace(oldValue, value, index));
 1178
 841179            AddStackTrace();
 1021180        }
 1181
 1182        /// <summary>
 1183        ///     Sorts the elements in the entire <see cref="ScriptableList{T}" /> using the specified comparer.
 1184        /// </summary>
 1185        /// <param name="comparer">
 1186        ///     The <see cref="IComparer{T}" /> implementation to use when comparing elements, or <c>null</c> to use
 1187        ///     the default comparer <see cref="Comparer{T}.Default" />.
 1188        /// </param>
 1189        /// <exception cref="InvalidOperationException">
 1190        ///     <paramref name="comparer" /> is <c>null</c>, and the default comparer cannot find an
 1191        ///     implementation of the <see cref="IComparer{T}" /> generic interface or the <see cref="IComparable" /> in
 1192        ///     type <typeparamref name="T" />.
 1193        /// </exception>
 1194        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 1195        public void Sort(IComparer<T>? comparer = null)
 1741196        {
 1741197            SortInternal(0, list.Count, comparer, null);
 1261198        }
 1199
 1200        /// <summary>
 1201        ///     Sorts the elements in a range of elements in list using the specified comparer.
 1202        /// </summary>
 1203        /// <param name="index">The starting index of the range to sort.</param>
 1204        /// <param name="count">The number of elements in the range to sort.</param>
 1205        /// <param name="comparer">
 1206        ///     The <see cref="IComparer{T}" /> implementation to use when comparing elements, or <c>null</c> to use
 1207        ///     the default comparer <see cref="Comparer{T}.Default" />.
 1208        /// </param>
 1209        /// <exception cref="InvalidOperationException">
 1210        ///     <paramref name="comparer" /> is <c>null</c>, and the default comparer cannot find an
 1211        ///     implementation of the <see cref="IComparer{T}" /> generic interface or the <see cref="IComparable" /> in
 1212        ///     type <typeparamref name="T" />.
 1213        /// </exception>
 1214        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 1215        public void Sort(int index, int count, IComparer<T>? comparer = null)
 781216        {
 781217            SortInternal(index, count, comparer, null);
 541218        }
 1219
 1220        /// <summary>
 1221        ///     Sorts the elements in the entire list using the specified <see cref="Comparison{T}" />.
 1222        /// </summary>
 1223        /// <param name="comparison">The <see cref="Comparison{T}" /> to use when comparing elements.</param>
 1224        /// <exception cref="ArgumentNullException"><paramref name="comparison" /> is <c>null</c>.</exception>
 1225        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 1226        public void Sort(Comparison<T> comparison)
 721227        {
 721228            Guard.IsNotNull(comparison, nameof(comparison));
 1229
 721230            SortInternal(0, list.Count, null, comparison);
 481231        }
 1232
 1233        /// <summary>
 1234        ///     Internal sort method that skips a frame in the stack traces.
 1235        /// </summary>
 1236        /// <param name="index">The zero-based starting index of the range to sort.</param>
 1237        /// <param name="count">The length of the range to sort.</param>
 1238        /// <param name="comparer">
 1239        ///     The <see cref="IComparer{T}" /> implementation to use when comparing elements, or null to use
 1240        ///     the default comparer <see cref="Comparer{T}.Default" />.
 1241        /// </param>
 1242        /// <param name="comparison">The <see cref="Comparison{T}" /> to use when comparing elements.</param>
 1243        /// <exception cref="ArgumentOutOfRangeException"><c>index</c> is less than 0 or <c>count</c> is less than 0.</e
 1244        /// <exception cref="System.Data.ReadOnlyException">If the object is read-only and the application is playing.</
 1245        private void SortInternal(int index, int count, IComparer<T>? comparer, Comparison<T>? comparison)
 3241246        {
 1247            // If the game is playing, we don't want to set the value if it's read only.
 3241248            Guard.IsNotReadOnly(this, name);
 1249
 2281250            if (list.Count == 0)
 721251            {
 721252                AddStackTrace(1);
 721253                return;
 1254            }
 1255
 1561256            using CollectionScope<T> oldItemsScope = CollectionScope<T>.FromListSlice(list, index, count);
 1257
 1258            // If there's a comparison, we sort using that. Otherwise, we use the comparer, or the default comparer if t
 1561259            if (comparison != null)
 301260            {
 301261                list.Sort(comparison);
 301262            }
 1263            else
 1261264            {
 1261265                list.Sort(index, count, comparer);
 1261266            }
 1267
 1561268            using CollectionScope<T> newItemsScope = CollectionScope<T>.FromListSlice(list, index, count);
 1561269            InvokeCollectionChanged(CollectionChangedArgs<T>.Replace(oldItemsScope.Span, newItemsScope.Span, index));
 1270
 1561271            AddStackTrace(1);
 3841272        }
 1273
 1274        /// <summary>
 1275        ///     Copies the elements of the <see cref="ScriptableList{T}" /> to a new array.
 1276        /// </summary>
 1277        /// <returns>An array containing copies of the elements of the <see cref="ScriptableList{T}" />.</returns>
 1278        public T[] ToArray()
 61279        {
 61280            return list.ToArray();
 61281        }
 1282
 1283        /// <inheritdoc />
 1284        public sealed override void TrimExcess()
 181285        {
 181286            list.TrimExcess();
 181287            UpdateCounts();
 1288
 181289            AddStackTrace();
 181290        }
 1291
 1292        /// <summary>
 1293        ///     Determines whether every element in the <see cref="ScriptableList{T}" /> matches the conditions defined 
 1294        ///     specified predicate.
 1295        /// </summary>
 1296        /// <param name="match">The <see cref="Predicate{T}" /> delegate that defines the conditions to check against th
 1297        /// <returns>
 1298        ///     <c>true</c> if every element in the <see cref="ScriptableList{T}" /> matches the conditions defined by t
 1299        ///     specified predicate; otherwise, <c>false</c>. If the list has no elements, the return value is <c>true</
 1300        /// </returns>
 1301        /// <exception cref="ArgumentNullException"><paramref name="match" /> is <c>null</c>.</exception>
 1302        public bool TrueForAll(Predicate<T> match)
 61303        {
 61304            return list.TrueForAll(match);
 61305        }
 1306
 1307        /// <summary>
 1308        ///     Tries to find an element in the list that matches the specified predicate.
 1309        /// </summary>
 1310        /// <param name="match">The <see cref="Predicate{T}" /> delegate that defines the condition to check against.</p
 1311        /// <param name="result">
 1312        ///     The first result that matches the delegate. If no element was found, this will be the default
 1313        ///     value.
 1314        /// </param>
 1315        /// <returns><c>true</c> if an element was found; otherwise, <c>false</c>.</returns>
 1316        public bool TryFind(Predicate<T> match, out T? result)
 121317        {
 121318            int count = list.Count;
 841319            for (int i = 0; i < count; i++)
 361320            {
 1321                // Check if the element matches the predicate.
 361322                if (match(list[i]))
 61323                {
 1324                    // The element matches the predicate, so we can return it.
 61325                    result = list[i];
 61326                    return true;
 1327                }
 301328            }
 1329
 1330            // There was no element that matched the predicate.
 61331            result = default;
 61332            return false;
 121333        }
 1334
 1335        private void InvokeCollectionChanged(in CollectionChangedArgs<T> args)
 85141336        {
 85141337            OnCollectionChanged?.Invoke(args);
 85141338            if (!IsCollectionChangedNull())
 1321339            {
 1321340                InvokeCollectionChangedBase(args.ToNotifyCollectionChangedEventArgs());
 1321341            }
 85141342        }
 1343
 1344        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1345        private void UpdateCounts()
 82321346        {
 1347            // Update the count and capacity properties.
 82321348            Count = list.Count;
 82321349            Capacity = list.Capacity;
 82321350        }
 1351
 1352        /// <inheritdoc />
 1353        protected override void OnPreStart()
 22301354        {
 22301355            ClearSubscribers();
 1356
 22301357            if (clearOnStart && !isReadOnly)
 22121358            {
 22121359                list.Clear();
 22121360            }
 1361
 22301362            internalCount = list.Count;
 22301363            internalCapacity = list.Capacity;
 22301364        }
 1365
 1366        /// <summary>
 1367        ///     Warns if there are any left-over subscribers to the events.
 1368        /// </summary>
 1369        /// <remarks>This will only be called in the Unity editor and builds with the DEBUG flag.</remarks>
 1370        protected override void WarnIfLeftOverSubscribers()
 22121371        {
 22121372            base.WarnIfLeftOverSubscribers();
 22121373            EventHelper.WarnIfLeftOverSubscribers(OnCollectionChanged, nameof(OnCollectionChanged), this);
 22121374        }
 1375
 1376        /// <summary>
 1377        ///     Warns if there are any left-over objects in the dictionary.
 1378        /// </summary>
 1379        /// <remarks>This will only be called in the Unity editor and builds with the DEBUG flag.</remarks>
 1380        [Conditional("DEBUG")]
 1381        protected void WarnIfLeftOverObjects()
 22061382        {
 22061383            if (!isReadOnly && clearOnStart && list.Count > 0)
 61384            {
 61385                Debug.LogWarning($"There are left over objects in the scriptable list {name}. You should clear the list 
 61386            }
 22061387        }
 1388
 1389        /// <summary>
 1390        ///     Removes any subscribers from the event.
 1391        /// </summary>
 1392        /// <param name="warnIfLeftOver">
 1393        ///     If true, a warning will be printed in the console if there are any subscribers.
 1394        ///     The warning will only be printed in the editor and debug builds.
 1395        /// </param>
 1396        public void ClearSubscribers(bool warnIfLeftOver = false)
 22361397        {
 1398#if DEBUG
 22361399            if (warnIfLeftOver)
 61400            {
 61401                WarnIfLeftOverSubscribers();
 61402            }
 1403#endif
 22361404            OnCollectionChanged = null;
 22361405            ClearBaseSubscribers();
 22361406        }
 1407
 1408        /// <inheritdoc />
 1409        protected override void OnDisabled()
 22061410        {
 22061411            WarnIfLeftOverObjects();
 22061412            WarnIfLeftOverSubscribers();
 1413
 22061414            list.TrimExcess();
 22061415        }
 1416
 1417        #region Obsolete
 1418#if UNITY_EDITOR // Don't include in build.
 1419#pragma warning disable CS0067 // Event is never used
 1420        /// <summary>
 1421        ///     Called when something was added. Gives you the newly added item.
 1422        /// </summary>
 1423        [Obsolete("Use 'OnCollectionChanged' instead. This will be removed in build.", true)]
 1424        public event Action<T>? OnAdded;
 1425        /// <summary>
 1426        ///     Called when something was inserted. Gives you the index it was inserted at and the newly inserted item.
 1427        /// </summary>
 1428        [Obsolete("Use 'OnCollectionChanged' instead. This will be removed in build.", true)]
 1429        public event Action<int, T>? OnInserted;
 1430        /// <summary>
 1431        ///     Called when something was added or inserted. Gives you the index it was added/inserted at and the newly
 1432        ///     added/inserted item.
 1433        /// </summary>
 1434        [Obsolete("Use 'OnCollectionChanged' instead. This will be removed in build.", true)]
 1435        public event Action<int, T>? OnAddedOrInserted;
 1436        /// <summary>
 1437        ///     Called when something was set using the indexer. Gives you the index it was set at, the old value and th
 1438        ///     value.
 1439        /// </summary>
 1440        [Obsolete("Use 'OnCollectionChanged' instead. This will be removed in build.", true)]
 1441        public event Action<int, T, T>? OnSet;
 1442        /// <summary>
 1443        ///     Called when something was removed. Gives you the index it was removed at and the removed item.
 1444        /// </summary>
 1445        [Obsolete("Use 'OnCollectionChanged' instead. This will be removed in build.", true)]
 1446        public event Action<int, T>? OnRemoved;
 1447        /// <summary>
 1448        ///     Called when the list is cleared.
 1449        /// </summary>
 1450        [Obsolete("Use 'OnCollectionChanged' instead. This will be removed in build.", true)]
 1451        public event Action? OnCleared;
 1452        /// <summary>
 1453        ///     Called when the list is changed in any way.
 1454        /// </summary>
 1455        [Obsolete("Use 'OnCollectionChanged' instead. This will be removed in build.", true)]
 1456        public event Action<ListChangeType>? OnChanged;
 1457#pragma warning restore CS0067 // Event is never used
 1458#endif // UNITY_EDITOR
 1459        #endregion
 1460    }
 1461}

Methods/Properties

GetViewHashCode()
ScriptableList()
Capacity()
Capacity(System.Int32)
Count()
Count(System.Int32)
Item(System.Int32)
Item(System.Int32, T)
get_Item(System.Int32)
set_Item(System.Int32, System.Object)
IsReadOnly()
IsReadOnly(System.Boolean)
SetEqualityCheck()
SetEqualityCheck(System.Boolean)
ClearOnStart()
ClearOnStart(System.Boolean)
get_IsSynchronized()
get_SyncRoot()
get_IsFixedSize()
Add(T)
Add(System.Object)
AddInternal(T)
AddRange(System.Collections.Generic.IEnumerable[T])
BinarySearch(T, System.Collections.Generic.IComparer[T])
BinarySearch(System.Int32, System.Int32, T, System.Collections.Generic.IComparer[T])
Clear()
Contains(T)
Contains(System.Object)
ConvertAll[TOutput](System.Converter[T,TOutput])
ConvertAll[TOutput](System.Collections.Generic.ICollection[TOutput], System.Converter[T,TOutput])
CopyTo()
CopyTo(, System.Int32)
CopyTo(System.Array, System.Int32)
EnsureCapacity(System.Int32)
Exists(System.Predicate[T])
Find(System.Predicate[T])
FindAll(System.Predicate[T])
FindAll(System.Collections.Generic.IList[T], System.Predicate[T])
FindIndex(System.Predicate[T])
FindIndex(System.Int32, System.Predicate[T])
FindIndex(System.Int32, System.Int32, System.Predicate[T])
FindLast(System.Predicate[T])
FindLastIndex(System.Predicate[T])
FindLastIndex(System.Int32, System.Predicate[T])
FindLastIndex(System.Int32, System.Int32, System.Predicate[T])
ForEach(System.Action[T])
GetEnumerator()
System.Collections.Generic.IEnumerable<T>.GetEnumerator()
GetEnumerator()
GetRange(System.Int32, System.Int32)
GetRange(System.Int32, System.Int32, System.Collections.Generic.IList[T])
IndexOf(T)
IndexOf(T, System.Int32)
IndexOf(T, System.Int32, System.Int32)
IndexOf(System.Object)
Insert(System.Int32, T)
Insert(System.Int32, System.Object)
InsertInternal(System.Int32, T)
InsertRange(System.Int32, System.Collections.Generic.IEnumerable[T])
LastIndexOf(T)
LastIndexOf(T, System.Int32)
LastIndexOf(T, System.Int32, System.Int32)
Remove(T)
Remove(System.Object)
RemoveInternal(T)
RemoveAll(System.Predicate[T])
RemoveAllWithPredicate(System.Span[T], System.Int32&, System.Predicate[T])
RemoveAt(System.Int32)
RemoveRange(System.Int32, System.Int32)
Reverse()
Reverse(System.Int32, System.Int32)
ReverseInternal(System.Int32, System.Int32)
Slice(System.Int32, System.Int32)
Slice(System.Int32, System.Int32, System.Collections.Generic.IList[T])
SetValue(System.Int32, T)
Sort(System.Collections.Generic.IComparer[T])
Sort(System.Int32, System.Int32, System.Collections.Generic.IComparer[T])
Sort(System.Comparison[T])
SortInternal(System.Int32, System.Int32, System.Collections.Generic.IComparer[T], System.Comparison[T])
ToArray()
TrimExcess()
TrueForAll(System.Predicate[T])
TryFind(System.Predicate[T], )
InvokeCollectionChanged()
UpdateCounts()
OnPreStart()
WarnIfLeftOverSubscribers()
WarnIfLeftOverObjects()
ClearSubscribers(System.Boolean)
OnDisabled()