Files
Just.Core/Core/Collections/ImmutableSequence.cs
T
just 566c813e8d
.NET Test / .NET tests (push) Failing after 1m54s
base32 refactoring
2026-07-10 21:53:37 +04:00

200 lines
8.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections;
using System.Collections.Immutable;
namespace Just.Core.Collections;
/// <summary>
/// Represents an immutable, ordered sequence of items with valueequality 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 valuebased 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>,
IEquatable<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 readonly 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 zerobased index.
/// </summary>
/// <param name="index">The zerobased 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
{
var (offset, count) = range.GetOffsetAndLength(_values.Count);
return ConstructNew(_values.GetRange(offset, count));
}
}
/// <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 elementwise 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)
{
return false;
}
for (int i = 0; i < _values.Count; i++)
{
if (!CompareItem(_values[i], other._values[i]))
{
return false;
}
}
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();
foreach (var value in _values)
{
hash.Add(value);
}
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);
}