@@ -3,6 +3,22 @@ using System.Collections.Immutable;
|
||||
|
||||
namespace Just.Core.Collections;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an immutable, ordered sequence of items with value‑equality semantics.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of elements in the sequence.</typeparam>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This class is a thin wrapper around <see cref="ImmutableList{T}"/> that implements
|
||||
/// <see cref="IReadOnlyList{T}"/>, <see cref="IEquatable{T}"/>, and value‑based equality.
|
||||
/// All modifications return new <see cref="ImmutableSequence{T}"/> instances, leaving the
|
||||
/// original unchanged.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Subclasses may override <see cref="ConstructNew"/> to ensure mutation methods return
|
||||
/// the correct derived type.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class ImmutableSequence<T> :
|
||||
IEnumerable<T>,
|
||||
IReadOnlyList<T>,
|
||||
@@ -11,17 +27,61 @@ public class ImmutableSequence<T> :
|
||||
private static readonly Func<T?, T?, bool> CompareItem = EqualityComparer<T>.Default.Equals;
|
||||
private readonly ImmutableList<T> _values;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new empty instance of the <see cref="ImmutableSequence{T}"/> class.
|
||||
/// </summary>
|
||||
public ImmutableSequence() => _values = [];
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImmutableSequence{T}"/> class that
|
||||
/// wraps the specified <see cref="ImmutableList{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="values">The immutable list to wrap.</param>
|
||||
public ImmutableSequence(ImmutableList<T> values) => _values = values;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImmutableSequence{T}"/> class with
|
||||
/// the elements from the provided enumerable sequence.
|
||||
/// </summary>
|
||||
/// <param name="values">The items to include in the sequence.</param>
|
||||
public ImmutableSequence(IEnumerable<T> values) => _values = [..values];
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImmutableSequence{T}"/> class with
|
||||
/// the elements from the provided read‑only span.
|
||||
/// </summary>
|
||||
/// <param name="values">The items to include in the sequence.</param>
|
||||
public ImmutableSequence(ReadOnlySpan<T> values) : this(ImmutableList.Create(values))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the sequence contains any elements.
|
||||
/// </summary>
|
||||
public bool IsEmpty => _values.IsEmpty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of elements in the sequence.
|
||||
/// </summary>
|
||||
public int Count => _values.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the element at the specified zero‑based index.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero‑based index of the element to get.</param>
|
||||
/// <returns>The element at the specified index.</returns>
|
||||
public T this[int index] => _values[index];
|
||||
/// <summary>
|
||||
/// Gets the element at the specified position from the start or end of the sequence.
|
||||
/// </summary>
|
||||
/// <param name="index">An <see cref="Index"/> value (e.g., <c>^1</c> for the last element).</param>
|
||||
/// <returns>The element at the specified position.</returns>
|
||||
public T this[Index index] => _values[index];
|
||||
/// <summary>
|
||||
/// Gets a new <see cref="ImmutableSequence{T}"/> containing the elements in the specified range.
|
||||
/// </summary>
|
||||
/// <param name="range">The range of elements to include.</param>
|
||||
/// <returns>A new sequence representing the slice.</returns>
|
||||
public ImmutableSequence<T> this[Range range]
|
||||
{
|
||||
get
|
||||
@@ -31,25 +91,60 @@ public class ImmutableSequence<T> :
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ImmutableSequence{T}"/> from the provided immutable list.
|
||||
/// Subclasses can override this to return instances of a more specific type.
|
||||
/// </summary>
|
||||
/// <param name="values">The immutable list that will become the internal storage.</param>
|
||||
/// <returns>A new sequence containing the given items.</returns>
|
||||
protected virtual ImmutableSequence<T> ConstructNew(ImmutableList<T> values) => [..values];
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new sequence with the specified value appended to the end.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to add.</param>
|
||||
/// <returns>A new sequence containing the original items followed by <paramref name="value"/>.</returns>
|
||||
public ImmutableSequence<T> Add(T value) => ConstructNew(_values.Add(value));
|
||||
/// <summary>
|
||||
/// Returns a new sequence with the specified value inserted at the beginning.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to add.</param>
|
||||
/// <returns>A new sequence that starts with <paramref name="value"/> and then contains the original items.</returns>
|
||||
public ImmutableSequence<T> AddFront(T value) => ConstructNew(_values.Insert(0, value));
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through the sequence.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="ImmutableList{T}.Enumerator"/> value type enumerator.</returns>
|
||||
public ImmutableList<T>.Enumerator GetEnumerator() => _values.GetEnumerator();
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => ((IEnumerable<T>)_values).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_values).GetEnumerator();
|
||||
|
||||
public override string ToString() => string.Join(Environment.NewLine, _values);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this sequence is equal to another <see cref="ImmutableSequence{T}"/>.
|
||||
/// Equality is based on the number of elements and the element‑wise equality comparison
|
||||
/// using the default equality comparer for <typeparamref name="T"/>.
|
||||
/// </summary>
|
||||
/// <param name="other">The sequence to compare with this instance. Can be <c>null</c>.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the sequences have the same length and all elements are equal;
|
||||
/// <c>false</c> otherwise.
|
||||
/// </returns>
|
||||
public virtual bool Equals([NotNullWhen(true)] ImmutableSequence<T>? other)
|
||||
{
|
||||
if (other is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_values.Count != other?._values.Count)
|
||||
if (_values.Count != other._values.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -65,7 +160,16 @@ public class ImmutableSequence<T> :
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified object is equal to the current sequence.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare with the current sequence.</param>
|
||||
/// <returns><c>true</c> if <paramref name="obj"/> is an <see cref="ImmutableSequence{T}"/> and equals this instance; otherwise <c>false</c>.</returns>
|
||||
public override bool Equals([NotNullWhen(true)] object? obj) => Equals(obj as ImmutableSequence<T>);
|
||||
/// <summary>
|
||||
/// Serves as a hash function for the sequence.
|
||||
/// </summary>
|
||||
/// <returns>A hash code that incorporates all elements in order.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
HashCode hash = new();
|
||||
@@ -78,6 +182,18 @@ public class ImmutableSequence<T> :
|
||||
return hash.ToHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether two sequences are equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first sequence to compare.</param>
|
||||
/// <param name="right">The second sequence to compare.</param>
|
||||
/// <returns><c>true</c> if both sequences are <c>null</c> or they are considered equal; otherwise <c>false</c>.</returns>
|
||||
public static bool operator ==(ImmutableSequence<T>? left, ImmutableSequence<T>? right) => left is null ? right is null : left.Equals(right);
|
||||
/// <summary>
|
||||
/// Determines whether two sequences are not equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first sequence to compare.</param>
|
||||
/// <param name="right">The second sequence to compare.</param>
|
||||
/// <returns><c>true</c> if the sequences are not equal; otherwise <c>false</c>.</returns>
|
||||
public static bool operator !=(ImmutableSequence<T>? left, ImmutableSequence<T>? right) => !(left == right);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user