< Summary

Information
Class: SwiftCollections.Observable.SwiftObservableList<T>
Assembly: SwiftCollections
File(s): /home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Observable/SwiftObservableList.cs
Line coverage
100%
Covered lines: 81
Uncovered lines: 0
Coverable lines: 81
Total lines: 292
Line coverage: 100%
Branch coverage
100%
Covered branches: 26
Total branches: 26
Branch coverage: 100%
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(...)100%66100%
AddKnownCountRange(...)100%44100%
AddRange(...)100%11100%
AddRange(...)100%22100%
EnsureAdditionalCapacity(...)100%22100%
Remove(...)100%22100%
RemoveAt(...)100%11100%
RemoveAll(...)100%22100%
Insert(...)100%11100%
Clear()100%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 System;
 9using System.Collections.Generic;
 10using System.Collections.Specialized;
 11using System.ComponentModel;
 12using System.Text.Json.Serialization;
 13using Chronicler;
 14using Chronicler.Diagnostics;
 15using Chronicler.Serialization;
 16using MemoryPack;
 17
 18namespace SwiftCollections.Observable;
 19
 20/// <summary>
 21/// Represents an observable extension of the high-performance <see cref="SwiftList{T}"/>.
 22/// Notifies listeners of changes to its items and structure for reactive programming scenarios.
 23/// </summary>
 24/// <typeparam name="T">The type of elements in the list.</typeparam>
 25[Serializable]
 26[JsonConverter(typeof(StateJsonConverterFactory))]
 27[MemoryPackable]
 28public partial class SwiftObservableList<T> : SwiftList<T>, IStateBacked<SwiftArrayState<T>>, INotifyPropertyChanged, IN
 29{
 30    #region Events
 31
 32    /// <summary>
 33    /// Raised when a property on the list changes.
 34    /// </summary>
 35    public event PropertyChangedEventHandler? PropertyChanged;
 36
 37    /// <summary>
 38    /// Raised when the list's collection is modified.
 39    /// </summary>
 40    public event NotifyCollectionChangedEventHandler? CollectionChanged;
 41
 42    #endregion
 43
 44    #region Constructors
 45
 46    /// <summary>
 47    /// Initializes a new instance of the <see cref="SwiftObservableList{T}"/> class.
 48    /// </summary>
 7249    public SwiftObservableList() : base() { }
 50
 51    /// <summary>
 52    /// Initializes a new instance of the <see cref="SwiftObservableList{T}"/> class with the specified initial capacity
 53    /// </summary>
 254    public SwiftObservableList(int capacity) : base(capacity) { }
 55
 56    /// <summary>
 57    /// Initializes a new instance of the <see cref="SwiftObservableList{T}"/> class that contains elements copied from 
 58    /// </summary>
 5659    public SwiftObservableList(IEnumerable<T> collection) : base(collection) { }
 60
 61    ///  <summary>
 62    ///  Initializes a new instance of the <see cref="SwiftObservableList{T}"/> class with the specified <see cref="Swif
 63    ///  </summary>
 64    ///  <param name="state">The state containing the internal array, count, offset, and version for initialization.</pa
 65    [MemoryPackConstructor]
 1266    public SwiftObservableList(SwiftArrayState<T> state) : base(state) { }
 67
 68    #endregion
 69
 70    #region Properties
 71
 72    /// <summary>
 73    /// Sets or gets the element at the specified index.
 74    /// Raises collection change notifications on modification.
 75    /// </summary>
 76    [JsonIgnore]
 77    [MemoryPackIgnore]
 78    public new T this[int index]
 79    {
 50680        get => base[index];
 81        set
 82        {
 150683            T oldValue = base[index];
 150484            if (!Equals(oldValue, value))
 85            {
 150186                base[index] = value;
 150187                OnCollectionChanged(NotifyCollectionChangedAction.Replace, oldValue, value, index);
 150188                OnPropertyChanged("InnerArray[]");
 89            }
 150490        }
 91    }
 92
 93    #endregion
 94
 95    #region Methods
 96
 97    /// <summary>
 98    /// Adds an element to the end of the list.
 99    /// Raises collection and property change notifications.
 100    /// </summary>
 101    public override void Add(T item)
 102    {
 11064103        base.Add(item);
 11064104        OnCollectionChanged(NotifyCollectionChangedAction.Add, item, _count - 1);
 11064105        OnPropertyChanged(nameof(Count));
 11064106    }
 107
 108    /// <summary>
 109    /// Adds the elements of the specified collection to the end of the current collection.
 110    /// </summary>
 111    /// <remarks>
 112    /// Known-count sources reserve capacity before enumeration while preserving per-item notifications.
 113    /// </remarks>
 114    /// <param name="items">The collection whose elements should be added to the end of the collection. Cannot be null.<
 115    public override void AddRange(IEnumerable<T> items)
 116    {
 4117        ChronicleGuard.ThrowIfNull(items, nameof(items));
 118
 4119        if (items is ICollection<T> collection)
 120        {
 1121            AddKnownCountRange(collection, collection.Count);
 1122            return;
 123        }
 124
 3125        if (items is IReadOnlyCollection<T> readOnlyCollection)
 126        {
 2127            AddKnownCountRange(readOnlyCollection, readOnlyCollection.Count);
 2128            return;
 129        }
 130
 4131        foreach (T item in items)
 1132            Add(item);
 1133    }
 134
 135    private void AddKnownCountRange(IEnumerable<T> items, int count)
 136    {
 3137        if (count > 0)
 138        {
 2139            EnsureAdditionalCapacity(count);
 140
 28141            foreach (T item in items)
 12142                Add(item);
 143        }
 3144    }
 145
 146    /// <summary>
 147    /// Adds the elements of the specified array to the end of the list.
 148    /// Raises collection and property change notifications for each added item.
 149    /// </summary>
 150    public override void AddRange(T[] items)
 151    {
 2152        ChronicleGuard.ThrowIfNull(items, nameof(items));
 1153        AddRange(items.AsSpan());
 1154    }
 155
 156    /// <summary>
 157    /// Adds the elements of the specified span to the end of the list.
 158    /// Raises collection and property change notifications for each added item.
 159    /// </summary>
 160    public override void AddRange(ReadOnlySpan<T> items)
 161    {
 3162        EnsureAdditionalCapacity(items.Length);
 163
 18164        for (int i = 0; i < items.Length; i++)
 6165            Add(items[i]);
 3166    }
 167
 168    private void EnsureAdditionalCapacity(int additionalCount)
 169    {
 5170        if (additionalCount > 0)
 171        {
 4172            long requiredCount = (long)_count + additionalCount;
 4173            ChronicleGuard.ThrowIfTrue(requiredCount > int.MaxValue, message: "The collection is too large.");
 4174            EnsureCapacity((int)requiredCount);
 175        }
 5176    }
 177
 178    /// <summary>
 179    /// Removes the first occurrence of a specific object from the list.
 180    /// Raises collection and property change notifications.
 181    /// </summary>
 182    public override bool Remove(T item)
 183    {
 503184        int index = IndexOf(item);
 503185        if (index >= 0)
 186        {
 502187            var removedItem = this[index];
 502188            base.RemoveAt(index);
 502189            OnCollectionChanged(NotifyCollectionChangedAction.Remove, removedItem, index);
 502190            OnPropertyChanged(nameof(Count));
 502191            return true;
 192        }
 193
 1194        return false;
 195    }
 196
 197    /// <summary>
 198    /// Removes the element at the specified index.
 199    /// Raises collection and property change notifications.
 200    /// </summary>
 201    public override void RemoveAt(int index)
 202    {
 3203        var removedItem = this[index];
 1204        base.RemoveAt(index);
 1205        OnCollectionChanged(NotifyCollectionChangedAction.Remove, removedItem, index);
 1206        OnPropertyChanged(nameof(Count));
 1207    }
 208
 209    /// <summary>
 210    /// Removes all elements from the collection that match the conditions defined by the specified predicate.
 211    /// </summary>
 212    /// <remarks>
 213    /// Raises a collection changed event with the Reset action and notifies property changes if any elements are remove
 214    /// This method is useful when you need to remove multiple items based on a condition and notify observers of the ch
 215    /// </remarks>
 216    /// <param name="match">The delegate that defines the conditions of the elements to remove. Cannot be null.</param>
 217    /// <returns>The number of elements removed from the collection.</returns>
 218    public override int RemoveAll(Predicate<T> match)
 219    {
 6220        int removedCount = base.RemoveAll(match);
 221
 5222        if (removedCount > 0)
 223        {
 3224            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
 3225            OnPropertyChanged(nameof(Count));
 226        }
 227
 5228        return removedCount;
 229    }
 230
 231    /// <summary>
 232    /// Inserts an element into the list at the specified index.
 233    /// Raises collection and property change notifications.
 234    /// </summary>
 235    public override void Insert(int index, T item)
 236    {
 1237        base.Insert(index, item);
 1238        OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index);
 1239        OnPropertyChanged(nameof(Count));
 1240    }
 241
 242    /// <summary>
 243    /// Clears all elements from the list.
 244    /// Raises a reset collection change notification.
 245    /// </summary>
 246    public override void Clear()
 247    {
 8248        if (_count > 0)
 249        {
 3250            base.Clear();
 3251            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
 3252            OnPropertyChanged(nameof(Count));
 253        }
 8254    }
 255
 256    #endregion
 257
 258    #region Notifications
 259
 260    /// <summary>
 261    /// Raises the <see cref="PropertyChanged"/> event.
 262    /// </summary>
 263    /// <param name="propertyName">The name of the property that changed.</param>
 264    protected virtual void OnPropertyChanged(string propertyName)
 265    {
 13075266        var handler = PropertyChanged;
 13075267        handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
 14268    }
 269
 270    /// <summary>
 271    /// Raises the <see cref="CollectionChanged"/> event for the specified action and items.
 272    /// </summary>
 273    protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
 274    {
 13075275        var handler = CollectionChanged;
 13075276        handler?.Invoke(this, args);
 10032277    }
 278
 279    private void OnCollectionChanged(NotifyCollectionChangedAction action, T item, int index)
 280    {
 11568281        var args = new NotifyCollectionChangedEventArgs(action, item, index);
 11568282        OnCollectionChanged(args);
 11568283    }
 284
 285    private void OnCollectionChanged(NotifyCollectionChangedAction action, T oldItem, T newItem, int index)
 286    {
 1501287        var args = new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index);
 1501288        OnCollectionChanged(args);
 1501289    }
 290
 291    #endregion
 292}