diff --git a/Core/Collections/VirtualList.cs b/Core/Collections/VirtualList.cs new file mode 100644 index 00000000..4db3de9f --- /dev/null +++ b/Core/Collections/VirtualList.cs @@ -0,0 +1,234 @@ +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Vanara.Collections; + +/// Interface that defines the methods for a virtual list. This interface is used by the class. +/// The type of the element. +/// +public interface IVirtualListMethods : IVirtualReadOnlyListMethods +{ + /// Adds an item to the end of the list. + /// The object to add to the list. + void AddItem(T item); + + /// Inserts an item to the list at the specified index. + /// The zero-based index at which item should be inserted. + /// The object to insert into the list. + void InsertItemAt(int index, T item); + + /// Removes the item at the specified index. + /// The zero-based index of the item to remove. + void RemoveItemAt(int index); + + /// Sets the element at the specified index. + /// The zero-based index of the element to set. + /// The element at the specified index. + void SetItemAt(int index, T value); +} + +/// +/// Interface that defines the methods for a virtual read-only list. This interface is used by the class. +/// +/// The type of the element. +public interface IVirtualReadOnlyListMethods +{ + /// Gets the number of elements in the collection. + /// The number of elements in the collection. + int GetItemCount(); + + /// Tries to get the element at the specified index. + /// The zero-based index of the element to get. + /// The value, if is a valid index; or if not. + /// if the list contains an element at the specified index; otherwise, . + bool TryGet(int index, [NotNullWhen(true)] out T? value); +} + +/// A virtual list that implements a lot of the scaffolding. +/// The element type. +public class VirtualList : VirtualReadOnlyList, IList +{ + /// The implementation. + protected readonly IVirtualListMethods impl; + + /// Initializes a new instance of the class. + public VirtualList(IVirtualListMethods impl) : base(impl) => this.impl = impl; + + /// + bool ICollection.IsReadOnly => false; + + /// + public new T this[int index] + { + get => base[index]; + set => impl.SetItemAt(index, value); + } + + /// + public void Add(T item) => impl.AddItem(item); + + /// + public void Clear() + { + for (int i = Count - 1; i >= 0; i--) + RemoveAt(i); + } + + /// + public void Insert(int index, T item) => impl.InsertItemAt(index, item); + + /// + public bool Remove(T item) + { + int i = IndexOf(item); + if (i >= 0) + { + RemoveAt(i); + return true; + } + return false; + } + + /// + public void RemoveAt(int index) => impl.RemoveItemAt(index); + + /// + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); +} + +/// Wrapper for that allows for the use of delegates instead of implementing the interface. +/// The element type. +/// +public class VirtualListMethodCarrier : IVirtualListMethods +{ + /// Initializes a new instance of the class. + /// Delegate that tries to get the element at the specified index. + /// Delegate that gets the number of elements in the collection. + /// Delegate that adds an item to the end of the list. + /// Delegate that inserts an item to the list at the specified index. + /// Delegate that removes the item at the specified index. + /// Delegate that sets the element at the specified index. + public VirtualListMethodCarrier(VirtualReadOnlyList.TryGetDelegate tryGet, Func getCount, Action? add = null, Action? insert = null, Action? removeAt = null, Action? setAt = null) + { + TryGet = tryGet; + GetCount = getCount; + Add = add; + Insert = insert; + RemoveAt = removeAt; + SetAt = setAt; + } + + /// Delegate that adds an item to the end of the list. + public Action? Add { get; } + + /// Delegate that gets the number of elements in the collection. + public Func GetCount { get; } + + /// Delegate that inserts an item to the list at the specified index. + public Action? Insert { get; } + + /// Delegate that removes the item at the specified index. + public Action? RemoveAt { get; } + + /// Delegate that sets the element at the specified index. + public Action? SetAt { get; } + + /// Delegate that tries to get the element at the specified index. + public VirtualReadOnlyList.TryGetDelegate TryGet { get; } + + /// + void IVirtualListMethods.AddItem(T item) => Add?.Invoke(item); + + /// + int IVirtualReadOnlyListMethods.GetItemCount() => GetCount(); + + /// + void IVirtualListMethods.InsertItemAt(int index, T item) => Insert?.Invoke(index, item); + + /// + void IVirtualListMethods.RemoveItemAt(int index) => RemoveAt?.Invoke(index); + + /// + void IVirtualListMethods.SetItemAt(int index, T value) => SetAt?.Invoke(index, value); + + /// + bool IVirtualReadOnlyListMethods.TryGet(int index, [NotNullWhen(true)] out T? value) => TryGet(index, out value); +} + +/// A virtual read-only list that implements a lot of the scaffolding. +/// The element type. +public class VirtualReadOnlyList : IReadOnlyList +{ + /// The read only implementation. + protected readonly IVirtualReadOnlyListMethods readOnlyImpl; + + /// Initializes a new instance of the class. + public VirtualReadOnlyList(IVirtualReadOnlyListMethods impl) => readOnlyImpl = impl; + + /// Delegate for a method that tries to get the element at the specified index. + /// The zero-based index of the element to get. + /// The value, if is a valid index; or if not. + /// if the list contains an element at the specified index; otherwise, . + public delegate bool TryGetDelegate(int index, [NotNullWhen(true)] out T? value); + + /// + public virtual int Count => readOnlyImpl.GetItemCount(); + + /// + public virtual T this[int index] => readOnlyImpl.TryGet(index, out T? v) ? v : throw new ArgumentOutOfRangeException(nameof(index)); + + /// + public virtual bool Contains(T item) => IndexOf(item) >= 0; + + /// + public virtual void CopyTo(T[] array, int arrayIndex) + { + if (array is null) + throw new ArgumentNullException(nameof(array)); + if (arrayIndex < 0 || arrayIndex > array.Length) + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + if (array.Length - arrayIndex < Count) + throw new ArgumentException("The number of elements in the source ICollection is greater than the available space from arrayIndex to the end of the destination array."); + + for (int i = 0; i < Count; i++) + array[arrayIndex + i] = this[i]; + } + + /// + public virtual IEnumerator GetEnumerator() + { + for (int i = 0; i < Count; i++) + yield return this[i]; + } + + /// + public virtual int IndexOf(T item) + { + for (int i = 0; i < Count; i++) + { + /* Unmerged change from project 'Vanara.PInvoke.Printing (net48)' + Before: + if (Equals(this[i], item)) + After: + { + if (Equals(this[i], item)) + */ + if (Equals(this[i], item)) + return i; + } + + /* Unmerged change from project 'Vanara.PInvoke.Printing (net48)' + Before: + return -1; + After: + } + + return -1; + */ + return -1; + } + + /// + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); +} \ No newline at end of file