< Summary

Information
Class: SwiftCollections.Observable.SwiftObservableArray<T>
Assembly: SwiftCollections
File(s): /home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Observable/SwiftObservableArray.cs
Line coverage
100%
Covered lines: 66
Uncovered lines: 0
Coverable lines: 66
Total lines: 270
Line coverage: 100%
Branch coverage
95%
Covered branches: 21
Total branches: 22
Branch coverage: 95.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
.ctor(...)100%22100%
.ctor(...)100%22100%
.ctor(...)100%11100%
get_Capacity()100%11100%
get_Item(...)100%11100%
set_Item(...)100%22100%
get_State()100%22100%
set_State(...)100%22100%
ToArray()100%22100%
ValidateIndex(...)100%44100%
OnItemChanged(...)75%44100%
OnPropertyChanged(...)100%22100%

File(s)

/home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Observable/SwiftObservableArray.cs

#LineLine coverage
 1//=======================================================================
 2// SwiftObservableArray.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024–present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using Chronicler;
 9using MemoryPack;
 10using SwiftCollections.Diagnostics;
 11using System;
 12using System.ComponentModel;
 13using System.Text.Json.Serialization;
 14
 15namespace SwiftCollections.Observable;
 16
 17/// <summary>
 18/// Represents an array of observable properties, raising events whenever an element is updated.
 19/// Designed for performance-critical scenarios in Unity game development.
 20/// </summary>
 21/// <typeparam name="TValue">The type of elements in the array.</typeparam>
 22[Serializable]
 23[JsonConverter(typeof(StateJsonConverterFactory))]
 24[MemoryPackable]
 25public partial class SwiftObservableArray<TValue> : IStateBacked<SwiftArrayState<TValue>>, INotifyPropertyChanged
 26{
 27    #region Fields
 28
 29    /// <summary>
 30    /// Represents the collection of observable properties managed by this instance.
 31    /// </summary>
 32    /// <remarks>Each element in the array corresponds to an individual observable property.
 33    /// The array may be null or empty if no properties are currently managed.
 34    /// </remarks>
 35    protected SwiftObservableProperty<TValue>[] _items;
 36
 37    /// <summary>
 38    /// Represents the event handler that is invoked when a property value changes on an item.
 39    /// </summary>
 40    /// <remarks>
 41    /// This handler is typically used to subscribe to property change notifications for items within a collection or co
 42    /// Derived classes can use this field to manage event subscriptions for item property changes.
 43    /// </remarks>
 44    protected PropertyChangedEventHandler _itemChangedHandler;
 45
 46    #endregion
 47
 48    #region Events
 49
 50    /// <summary>
 51    /// Raised when any element in the array changes, providing the index and the new value.
 52    /// </summary>
 53    public event EventHandler<ElementChangedEventArgs<TValue>>? ElementChanged;
 54
 55    /// <summary>
 56    /// Raised when the array's state changes.
 57    /// </summary>
 58    public event PropertyChangedEventHandler? PropertyChanged;
 59
 60    #endregion
 61
 62    #region Nested Types
 63
 64    /// <summary>
 65    /// Provides details about a changed element, including its index and new value.
 66    /// </summary>
 67    public class ElementChangedEventArgs<T> : EventArgs
 68    {
 69        /// <summary>
 70        /// The index of the changed element.
 71        /// </summary>
 72        public int Index { get; }
 73
 74        /// <summary>
 75        /// The new value of the changed element.
 76        /// </summary>
 77        public T NewValue { get; }
 78
 100879        internal ElementChangedEventArgs(int index, T newValue)
 80        {
 100881            Index = index;
 100882            NewValue = newValue;
 100883        }
 84    }
 85
 86    #endregion
 87
 88    #region Constructors
 89
 90    /// <summary>
 91    /// Initializes a new instance of the SwiftObservableArray class with the specified capacity.
 92    /// </summary>
 93    /// <remarks>
 94    /// Each element in the array is initialized with a new <see cref="SwiftObservableProperty{TValue}"/> instance.
 95    /// The capacity determines the fixed size of the array and cannot be changed after construction.
 96    /// </remarks>
 97    /// <param name="capacity">The number of elements the array can contain. Must be a positive integer.</param>
 2098    public SwiftObservableArray(int capacity)
 99    {
 20100        SwiftThrowHelper.ThrowIfNegativeOrZero(capacity, nameof(capacity));
 101
 20102        _items = new SwiftObservableProperty<TValue>[capacity];
 103        _itemChangedHandler = (sender, e) => OnItemChanged(sender);
 104
 202146105        for (int i = 0; i < capacity; i++)
 106        {
 101053107            _items[i] = new SwiftObservableProperty<TValue>
 101053108            {
 101053109                Index = i
 101053110            };
 101053111            _items[i].PropertyChanged += _itemChangedHandler;
 112        }
 20113    }
 114
 115    /// <summary>
 116    /// Initializes a new instance of the SwiftObservableArray class with the specified observable properties.
 117    /// </summary>
 118    /// <remarks>
 119    /// Each property in the array is assigned its index and subscribed to change notifications.
 120    /// Changes to any property will be observed by the array.
 121    /// </remarks>
 122    /// <param name="observableProperties">An array of <see cref="SwiftObservableProperty{TValue}"/> instances to be man
 2123    public SwiftObservableArray(SwiftObservableProperty<TValue>[] observableProperties)
 124    {
 2125        SwiftThrowHelper.ThrowIfNull(observableProperties, nameof(observableProperties));
 126
 1127        _items = observableProperties;
 128        _itemChangedHandler = (sender, e) => OnItemChanged(sender);
 129
 6130        for (int i = 0; i < _items.Length; i++)
 131        {
 2132            _items[i].Index = i;
 2133            _items[i].PropertyChanged += _itemChangedHandler;
 134        }
 1135    }
 136
 137    ///  <summary>
 138    ///  Initializes a new instance of the <see cref="SwiftObservableArray{TValue}"/> class with the specified <see cref
 139    ///  </summary>
 140    ///  <param name="state">The state containing the internal array, count, offset, and version for initialization.</pa
 141    [MemoryPackConstructor]
 7142    public SwiftObservableArray(SwiftArrayState<TValue> state)
 143    {
 7144        State = state;
 145
 7146        SwiftThrowHelper.ThrowIfNull(_items, nameof(_items));
 7147        SwiftThrowHelper.ThrowIfNull(_itemChangedHandler, nameof(_itemChangedHandler));
 7148    }
 149
 150    #endregion
 151
 152    #region Properties
 153
 154    /// <summary>
 155    /// Gets the total number of elements that the internal data structure can hold without resizing.
 156    /// </summary>
 157    [JsonIgnore]
 158    [MemoryPackIgnore]
 2159    public int Capacity => _items.Length;
 160
 161    /// <summary>
 162    /// Gets or sets the value at the specified index.
 163    /// </summary>
 164    /// <param name="index">The zero-based index of the element to get or set. Must be within the valid range of the col
 165    [JsonIgnore]
 166    [MemoryPackIgnore]
 167    public TValue this[int index]
 168    {
 169        get
 170        {
 10171            ValidateIndex(index);
 8172            return _items[index].Value;
 173        }
 174        set
 175        {
 1029176            ValidateIndex(index);
 1029177            if (!Equals(_items[index].Value, value))
 1027178                _items[index].Value = value;
 1029179        }
 180    }
 181
 182    /// <summary>
 183    /// Gets or sets the current state of the array, including the values of all items.
 184    /// </summary>
 185    /// <remarks>
 186    /// Setting this property replaces the entire array state, including all item values and their order.
 187    /// Any existing item change handlers are reset when the state is set.
 188    /// </remarks>
 189    [JsonInclude]
 190    [MemoryPackInclude]
 191    public SwiftArrayState<TValue> State
 192    {
 193        get
 194        {
 8195            var values = new TValue[_items.Length];
 196
 54197            for (int i = 0; i < _items.Length; i++)
 19198                values[i] = _items[i].Value;
 199
 8200            return new SwiftArrayState<TValue>(values);
 201        }
 202        internal set
 203        {
 7204            SwiftThrowHelper.ThrowIfNull(value.Items, nameof(value.Items));
 205
 7206            var values = value.Items;
 207
 7208            int capacity = values.Length;
 209
 7210            _items = new SwiftObservableProperty<TValue>[capacity];
 7211            _itemChangedHandler = (sender, e) => OnItemChanged(sender);
 212
 48213            for (int i = 0; i < capacity; i++)
 214            {
 17215                _items[i] = new SwiftObservableProperty<TValue>(values[i])
 17216                {
 17217                    Index = i
 17218                };
 17219                _items[i].PropertyChanged += _itemChangedHandler;
 220            }
 7221        }
 222    }
 223
 224    #endregion
 225
 226    #region Methods
 227
 228    /// <summary>
 229    /// Returns an array containing all elements in the collection.
 230    /// </summary>
 231    /// <returns>
 232    /// An array of type TValue that contains the values of the collection in order.
 233    /// The array will be empty if the collection contains no elements.
 234    /// </returns>
 235    public TValue[] ToArray()
 236    {
 7237        var array = new TValue[_items.Length];
 202046238        for (int i = 0; i < _items.Length; i++)
 101016239            array[i] = _items[i].Value;
 7240        return array;
 241    }
 242
 243    private void ValidateIndex(int index)
 244    {
 1039245        if (index < 0 || index >= _items.Length)
 2246            throw new IndexOutOfRangeException($"Index {index} is out of bounds for this array.");
 1037247    }
 248
 249    private void OnItemChanged(object? sender)
 250    {
 1028251        if (sender is SwiftObservableProperty<TValue> property)
 252        {
 1028253            int index = property.Index;
 254
 1028255            ElementChanged?.Invoke(
 1028256                this,
 1028257                new ElementChangedEventArgs<TValue>(index, property.Value)
 1028258            );
 259
 1028260            OnPropertyChanged("Items[]");
 261        }
 1028262    }
 263
 264    private void OnPropertyChanged(string propertyName)
 265    {
 1028266        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
 1267    }
 268
 269    #endregion
 270}