< Summary

Information
Class: SwiftCollections.Observable.SwiftObservableList<T>
Assembly: SwiftCollections
File(s): /home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Observable/SwiftObservableList.cs
Line coverage
93%
Covered lines: 78
Uncovered lines: 5
Coverable lines: 83
Total lines: 291
Line coverage: 93.9%
Branch coverage
76%
Covered branches: 20
Total branches: 26
Branch coverage: 76.9%
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%11100%
.ctor(...)100%11100%
.ctor(...)100%11100%
get_Item(...)100%11100%
set_Item(...)100%22100%
Add(...)100%11100%
AddRange(...)50%7670%
AddKnownCountRange(...)75%4483.33%
AddRange(...)100%11100%
AddRange(...)100%22100%
EnsureAdditionalCapacity(...)50%2283.33%
Remove(...)100%22100%
RemoveAt(...)100%11100%
RemoveAll(...)100%22100%
Insert(...)100%11100%
Clear()50%22100%
OnPropertyChanged(...)100%22100%
OnCollectionChanged(...)100%22100%
OnCollectionChanged(...)100%11100%
OnCollectionChanged(...)100%11100%

File(s)

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

#LineLine coverage
 1//=======================================================================
 2// SwiftObservableList.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.Collections.Generic;
 13using System.Collections.Specialized;
 14using System.ComponentModel;
 15using System.Text.Json.Serialization;
 16
 17namespace SwiftCollections.Observable;
 18
 19/// <summary>
 20/// Represents an observable extension of the high-performance <see cref="SwiftList{T}"/>.
 21/// Notifies listeners of changes to its items and structure for reactive programming scenarios.
 22/// </summary>
 23/// <typeparam name="T">The type of elements in the list.</typeparam>
 24[Serializable]
 25[JsonConverter(typeof(StateJsonConverterFactory))]
 26[MemoryPackable]
 27public partial class SwiftObservableList<T> : SwiftList<T>, IStateBacked<SwiftArrayState<T>>, INotifyPropertyChanged, IN
 28{
 29    #region Events
 30
 31    /// <summary>
 32    /// Raised when a property on the list changes.
 33    /// </summary>
 34    public event PropertyChangedEventHandler? PropertyChanged;
 35
 36    /// <summary>
 37    /// Raised when the list's collection is modified.
 38    /// </summary>
 39    public event NotifyCollectionChangedEventHandler? CollectionChanged;
 40
 41    #endregion
 42
 43    #region Constructors
 44
 45    /// <summary>
 46    /// Initializes a new instance of the <see cref="SwiftObservableList{T}"/> class.
 47    /// </summary>
 6648    public SwiftObservableList() : base() { }
 49
 50    /// <summary>
 51    /// Initializes a new instance of the <see cref="SwiftObservableList{T}"/> class with the specified initial capacity
 52    /// </summary>
 253    public SwiftObservableList(int capacity) : base(capacity) { }
 54
 55    /// <summary>
 56    /// Initializes a new instance of the <see cref="SwiftObservableList{T}"/> class that contains elements copied from 
 57    /// </summary>
 1258    public SwiftObservableList(IEnumerable<T> collection) : base(collection) { }
 59
 60    ///  <summary>
 61    ///  Initializes a new instance of the <see cref="SwiftObservableList{T}"/> class with the specified <see cref="Swif
 62    ///  </summary>
 63    ///  <param name="state">The state containing the internal array, count, offset, and version for initialization.</pa
 64    [MemoryPackConstructor]
 1265    public SwiftObservableList(SwiftArrayState<T> state) : base(state) { }
 66
 67    #endregion
 68
 69    #region Properties
 70
 71    /// <summary>
 72    /// Sets or gets the element at the specified index.
 73    /// Raises collection change notifications on modification.
 74    /// </summary>
 75    [JsonIgnore]
 76    [MemoryPackIgnore]
 77    public new T this[int index]
 78    {
 50679        get => base[index];
 80        set
 81        {
 150682            T oldValue = base[index];
 150483            if (!Equals(oldValue, value))
 84            {
 150185                base[index] = value;
 150186                OnCollectionChanged(NotifyCollectionChangedAction.Replace, oldValue, value, index);
 150187                OnPropertyChanged("InnerArray[]");
 88            }
 150489        }
 90    }
 91
 92    #endregion
 93
 94    #region Methods
 95
 96    /// <summary>
 97    /// Adds an element to the end of the list.
 98    /// Raises collection and property change notifications.
 99    /// </summary>
 100    public override void Add(T item)
 101    {
 11060102        base.Add(item);
 11060103        OnCollectionChanged(NotifyCollectionChangedAction.Add, item, _count - 1);
 11060104        OnPropertyChanged(nameof(Count));
 11060105    }
 106
 107    /// <summary>
 108    /// Adds the elements of the specified collection to the end of the current collection.
 109    /// </summary>
 110    /// <remarks>
 111    /// Known-count sources reserve capacity before enumeration while preserving per-item notifications.
 112    /// </remarks>
 113    /// <param name="items">The collection whose elements should be added to the end of the collection. Cannot be null.<
 114    public override void AddRange(IEnumerable<T> items)
 115    {
 2116        SwiftThrowHelper.ThrowIfNull(items, nameof(items));
 117
 2118        if (items is ICollection<T> collection)
 119        {
 1120            AddKnownCountRange(collection, collection.Count);
 1121            return;
 122        }
 123
 1124        if (items is IReadOnlyCollection<T> readOnlyCollection)
 125        {
 1126            AddKnownCountRange(readOnlyCollection, readOnlyCollection.Count);
 1127            return;
 128        }
 129
 0130        foreach (T item in items)
 0131            Add(item);
 0132    }
 133
 134    private void AddKnownCountRange(IEnumerable<T> items, int count)
 135    {
 2136        if (count == 0)
 0137            return;
 138
 2139        EnsureAdditionalCapacity(count);
 140
 28141        foreach (T item in items)
 12142            Add(item);
 2143    }
 144
 145    /// <summary>
 146    /// Adds the elements of the specified array to the end of the list.
 147    /// Raises collection and property change notifications for each added item.
 148    /// </summary>
 149    public override void AddRange(T[] items)
 150    {
 2151        SwiftThrowHelper.ThrowIfNull(items, nameof(items));
 1152        AddRange(items.AsSpan());
 1153    }
 154
 155    /// <summary>
 156    /// Adds the elements of the specified span to the end of the list.
 157    /// Raises collection and property change notifications for each added item.
 158    /// </summary>
 159    public override void AddRange(ReadOnlySpan<T> items)
 160    {
 2161        EnsureAdditionalCapacity(items.Length);
 162
 16163        for (int i = 0; i < items.Length; i++)
 6164            Add(items[i]);
 2165    }
 166
 167    private void EnsureAdditionalCapacity(int additionalCount)
 168    {
 4169        if (additionalCount == 0)
 0170            return;
 171
 4172        long requiredCount = (long)_count + additionalCount;
 4173        SwiftThrowHelper.ThrowIfTrue(requiredCount > int.MaxValue, message: "The collection is too large.");
 4174        EnsureCapacity((int)requiredCount);
 4175    }
 176
 177    /// <summary>
 178    /// Removes the first occurrence of a specific object from the list.
 179    /// Raises collection and property change notifications.
 180    /// </summary>
 181    public override bool Remove(T item)
 182    {
 503183        int index = IndexOf(item);
 503184        if (index >= 0)
 185        {
 502186            var removedItem = this[index];
 502187            base.RemoveAt(index);
 502188            OnCollectionChanged(NotifyCollectionChangedAction.Remove, removedItem, index);
 502189            OnPropertyChanged(nameof(Count));
 502190            return true;
 191        }
 192
 1193        return false;
 194    }
 195
 196    /// <summary>
 197    /// Removes the element at the specified index.
 198    /// Raises collection and property change notifications.
 199    /// </summary>
 200    public override void RemoveAt(int index)
 201    {
 3202        var removedItem = this[index];
 1203        base.RemoveAt(index);
 1204        OnCollectionChanged(NotifyCollectionChangedAction.Remove, removedItem, index);
 1205        OnPropertyChanged(nameof(Count));
 1206    }
 207
 208    /// <summary>
 209    /// Removes all elements from the collection that match the conditions defined by the specified predicate.
 210    /// </summary>
 211    /// <remarks>
 212    /// Raises a collection changed event with the Reset action and notifies property changes if any elements are remove
 213    /// This method is useful when you need to remove multiple items based on a condition and notify observers of the ch
 214    /// </remarks>
 215    /// <param name="match">The delegate that defines the conditions of the elements to remove. Cannot be null.</param>
 216    /// <returns>The number of elements removed from the collection.</returns>
 217    public override int RemoveAll(Predicate<T> match)
 218    {
 6219        int removedCount = base.RemoveAll(match);
 220
 5221        if (removedCount > 0)
 222        {
 3223            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
 3224            OnPropertyChanged(nameof(Count));
 225        }
 226
 5227        return removedCount;
 228    }
 229
 230    /// <summary>
 231    /// Inserts an element into the list at the specified index.
 232    /// Raises collection and property change notifications.
 233    /// </summary>
 234    public override void Insert(int index, T item)
 235    {
 1236        base.Insert(index, item);
 1237        OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index);
 1238        OnPropertyChanged(nameof(Count));
 1239    }
 240
 241    /// <summary>
 242    /// Clears all elements from the list.
 243    /// Raises a reset collection change notification.
 244    /// </summary>
 245    public override void Clear()
 246    {
 1247        if (_count > 0)
 248        {
 1249            base.Clear();
 1250            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
 1251            OnPropertyChanged(nameof(Count));
 252        }
 1253    }
 254
 255    #endregion
 256
 257    #region Notifications
 258
 259    /// <summary>
 260    /// Raises the <see cref="PropertyChanged"/> event.
 261    /// </summary>
 262    /// <param name="propertyName">The name of the property that changed.</param>
 263    protected virtual void OnPropertyChanged(string propertyName)
 264    {
 13069265        var handler = PropertyChanged;
 13069266        handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
 11267    }
 268
 269    /// <summary>
 270    /// Raises the <see cref="CollectionChanged"/> event for the specified action and items.
 271    /// </summary>
 272    protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
 273    {
 13069274        var handler = CollectionChanged;
 13069275        handler?.Invoke(this, args);
 10029276    }
 277
 278    private void OnCollectionChanged(NotifyCollectionChangedAction action, T item, int index)
 279    {
 11564280        var args = new NotifyCollectionChangedEventArgs(action, item, index);
 11564281        OnCollectionChanged(args);
 11564282    }
 283
 284    private void OnCollectionChanged(NotifyCollectionChangedAction action, T oldItem, T newItem, int index)
 285    {
 1501286        var args = new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index);
 1501287        OnCollectionChanged(args);
 1501288    }
 289
 290    #endregion
 291}