< 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: 70
Uncovered lines: 0
Coverable lines: 70
Total lines: 262
Line coverage: 100%
Branch coverage
100%
Covered branches: 22
Total branches: 22
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Index()100%11100%
get_NewValue()100%11100%
.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(...)100%44100%
OnPropertyChanged(...)100%22100%

File(s)

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

#LineLine coverage
 1using Chronicler;
 2using MemoryPack;
 3using System;
 4using System.ComponentModel;
 5using System.Text.Json.Serialization;
 6
 7namespace SwiftCollections.Observable;
 8
 9/// <summary>
 10/// Represents an array of observable properties, raising events whenever an element is updated.
 11/// Designed for performance-critical scenarios in Unity game development.
 12/// </summary>
 13/// <typeparam name="TValue">The type of elements in the array.</typeparam>
 14[Serializable]
 15[JsonConverter(typeof(StateJsonConverterFactory))]
 16[MemoryPackable]
 17public partial class SwiftObservableArray<TValue> : IStateBacked<SwiftArrayState<TValue>>, INotifyPropertyChanged
 18{
 19    #region Fields
 20
 21    /// <summary>
 22    /// Represents the collection of observable properties managed by this instance.
 23    /// </summary>
 24    /// <remarks>Each element in the array corresponds to an individual observable property.
 25    /// The array may be null or empty if no properties are currently managed.
 26    /// </remarks>
 27    protected SwiftObservableProperty<TValue>[] _items;
 28
 29    /// <summary>
 30    /// Represents the event handler that is invoked when a property value changes on an item.
 31    /// </summary>
 32    /// <remarks>
 33    /// This handler is typically used to subscribe to property change notifications for items within a collection or co
 34    /// Derived classes can use this field to manage event subscriptions for item property changes.
 35    /// </remarks>
 36    protected PropertyChangedEventHandler _itemChangedHandler;
 37
 38    #endregion
 39
 40    #region Events
 41
 42    /// <summary>
 43    /// Raised when any element in the array changes, providing the index and the new value.
 44    /// </summary>
 45    public event EventHandler<ElementChangedEventArgs<TValue>>? ElementChanged;
 46
 47    /// <summary>
 48    /// Raised when the array's state changes.
 49    /// </summary>
 50    public event PropertyChangedEventHandler? PropertyChanged;
 51
 52    #endregion
 53
 54    #region Nested Types
 55
 56    /// <summary>
 57    /// Provides details about a changed element, including its index and new value.
 58    /// </summary>
 59    public class ElementChangedEventArgs<T> : EventArgs
 60    {
 61        /// <summary>
 62        /// The index of the changed element.
 63        /// </summary>
 764        public int Index { get; }
 65
 66        /// <summary>
 67        /// The new value of the changed element.
 68        /// </summary>
 369        public T NewValue { get; }
 70
 100871        internal ElementChangedEventArgs(int index, T newValue)
 72        {
 100873            Index = index;
 100874            NewValue = newValue;
 100875        }
 76    }
 77
 78    #endregion
 79
 80    #region Constructors
 81
 82    /// <summary>
 83    /// Initializes a new instance of the SwiftObservableArray class with the specified capacity.
 84    /// </summary>
 85    /// <remarks>
 86    /// Each element in the array is initialized with a new <see cref="SwiftObservableProperty{TValue}"/> instance.
 87    /// The capacity determines the fixed size of the array and cannot be changed after construction.
 88    /// </remarks>
 89    /// <param name="capacity">The number of elements the array can contain. Must be a positive integer.</param>
 2090    public SwiftObservableArray(int capacity)
 91    {
 2092        SwiftThrowHelper.ThrowIfNegativeOrZero(capacity, nameof(capacity));
 93
 2094        _items = new SwiftObservableProperty<TValue>[capacity];
 104595        _itemChangedHandler = (sender, e) => OnItemChanged(sender);
 96
 20214697        for (int i = 0; i < capacity; i++)
 98        {
 10105399            _items[i] = new SwiftObservableProperty<TValue>
 101053100            {
 101053101                Index = i
 101053102            };
 101053103            _items[i].PropertyChanged += _itemChangedHandler;
 104        }
 20105    }
 106
 107    /// <summary>
 108    /// Initializes a new instance of the SwiftObservableArray class with the specified observable properties.
 109    /// </summary>
 110    /// <remarks>
 111    /// Each property in the array is assigned its index and subscribed to change notifications.
 112    /// Changes to any property will be observed by the array.
 113    /// </remarks>
 114    /// <param name="observableProperties">An array of <see cref="SwiftObservableProperty{TValue}"/> instances to be man
 2115    public SwiftObservableArray(SwiftObservableProperty<TValue>[] observableProperties)
 116    {
 2117        SwiftThrowHelper.ThrowIfNull(observableProperties, nameof(observableProperties));
 118
 1119        _items = observableProperties;
 2120        _itemChangedHandler = (sender, e) => OnItemChanged(sender);
 121
 6122        for (int i = 0; i < _items.Length; i++)
 123        {
 2124            _items[i].Index = i;
 2125            _items[i].PropertyChanged += _itemChangedHandler;
 126        }
 1127    }
 128
 129    ///  <summary>
 130    ///  Initializes a new instance of the <see cref="SwiftObservableArray{TValue}"/> class with the specified <see cref
 131    ///  </summary>
 132    ///  <param name="state">The state containing the internal array, count, offset, and version for initialization.</pa
 133    [MemoryPackConstructor]
 7134    public SwiftObservableArray(SwiftArrayState<TValue> state)
 135    {
 7136        State = state;
 137
 7138        SwiftThrowHelper.ThrowIfNull(_items, nameof(_items));
 7139        SwiftThrowHelper.ThrowIfNull(_itemChangedHandler, nameof(_itemChangedHandler));
 7140    }
 141
 142    #endregion
 143
 144    #region Properties
 145
 146    /// <summary>
 147    /// Gets the total number of elements that the internal data structure can hold without resizing.
 148    /// </summary>
 149    [JsonIgnore]
 150    [MemoryPackIgnore]
 2151    public int Capacity => _items.Length;
 152
 153    /// <summary>
 154    /// Gets or sets the value at the specified index.
 155    /// </summary>
 156    /// <param name="index">The zero-based index of the element to get or set. Must be within the valid range of the col
 157    [JsonIgnore]
 158    [MemoryPackIgnore]
 159    public TValue this[int index]
 160    {
 161        get
 162        {
 10163            ValidateIndex(index);
 8164            return _items[index].Value;
 165        }
 166        set
 167        {
 1029168            ValidateIndex(index);
 1029169            if (!Equals(_items[index].Value, value))
 1027170                _items[index].Value = value;
 1029171        }
 172    }
 173
 174    /// <summary>
 175    /// Gets or sets the current state of the array, including the values of all items.
 176    /// </summary>
 177    /// <remarks>
 178    /// Setting this property replaces the entire array state, including all item values and their order.
 179    /// Any existing item change handlers are reset when the state is set.
 180    /// </remarks>
 181    [JsonInclude]
 182    [MemoryPackInclude]
 183    public SwiftArrayState<TValue> State
 184    {
 185        get
 186        {
 8187            var values = new TValue[_items.Length];
 188
 54189            for (int i = 0; i < _items.Length; i++)
 19190                values[i] = _items[i].Value;
 191
 8192            return new SwiftArrayState<TValue>(values);
 193        }
 194        internal set
 195        {
 7196            SwiftThrowHelper.ThrowIfNull(value.Items, nameof(value.Items));
 197
 7198            var values = value.Items;
 199
 7200            int capacity = values.Length;
 201
 7202            _items = new SwiftObservableProperty<TValue>[capacity];
 9203            _itemChangedHandler = (sender, e) => OnItemChanged(sender);
 204
 48205            for (int i = 0; i < capacity; i++)
 206            {
 17207                _items[i] = new SwiftObservableProperty<TValue>(values[i])
 17208                {
 17209                    Index = i
 17210                };
 17211                _items[i].PropertyChanged += _itemChangedHandler;
 212            }
 7213        }
 214    }
 215
 216    #endregion
 217
 218    #region Methods
 219
 220    /// <summary>
 221    /// Returns an array containing all elements in the collection.
 222    /// </summary>
 223    /// <returns>
 224    /// An array of type TValue that contains the values of the collection in order.
 225    /// The array will be empty if the collection contains no elements.
 226    /// </returns>
 227    public TValue[] ToArray()
 228    {
 7229        var array = new TValue[_items.Length];
 202046230        for (int i = 0; i < _items.Length; i++)
 101016231            array[i] = _items[i].Value;
 7232        return array;
 233    }
 234
 235    private void ValidateIndex(int index)
 236    {
 1039237        if (index < 0 || index >= _items.Length)
 2238            throw new IndexOutOfRangeException($"Index {index} is out of bounds for this array.");
 1037239    }
 240
 241    private void OnItemChanged(object? sender)
 242    {
 1028243        if (sender is SwiftObservableProperty<TValue> property)
 244        {
 1028245            int index = property.Index;
 246
 1028247            ElementChanged?.Invoke(
 1028248                this,
 1028249                new ElementChangedEventArgs<TValue>(index, property.Value)
 1028250            );
 251
 1028252            OnPropertyChanged("Items[]");
 253        }
 1028254    }
 255
 256    private void OnPropertyChanged(string propertyName)
 257    {
 1028258        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
 1259    }
 260
 261    #endregion
 262}