DotNext by Roman Sakno

<PackageReference Include="DotNext" Version="1.2.6" />

 EnumerableTuple<I, T>

Represents tuple as enumerable collection.
using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace DotNext { [StructLayout(LayoutKind.Auto)] public readonly struct EnumerableTuple<I, T> : IReadOnlyList<I>, IEnumerable<I>, IEnumerable, IReadOnlyCollection<I> where T : IStructuralEquatable, IStructuralComparable { [StructLayout(LayoutKind.Auto)] public struct Enumerator : IEnumerator<I>, IEnumerator, IDisposable { private const int InitialPosition = -1; private T tuple; private readonly ValueRefFunc<T, int, I> accessor; private readonly int count; private int currentIndex; public I Current => accessor.Invoke(ref tuple, currentIndex); object IEnumerator.Current { get { return Current; } } internal Enumerator(T tuple, [In] [System.Runtime.CompilerServices.IsReadOnly] ref ValueRefFunc<T, int, I> accessor, int count) { this.tuple = tuple; currentIndex = -1; this.accessor = accessor; this.count = count; } public bool MoveNext() { return ++currentIndex < count; } public void Reset() { currentIndex = -1; } void IDisposable.Dispose() { this = default(Enumerator); } } private readonly T tuple; private readonly ValueRefFunc<T, int, I> accessor; public I this[int index] { get { if (!accessor.IsEmpty) return accessor.Invoke(ref Unsafe.AsRef<T>(ref tuple), index); throw new ArgumentOutOfRangeException("index"); } } public int Count { get; } internal EnumerableTuple(T tuple, int count, [In] [System.Runtime.CompilerServices.IsReadOnly] ref ValueRefFunc<T, int, I> accessor) { this.tuple = tuple; Count = count; this.accessor = accessor; } public Enumerator GetEnumerator() { return new Enumerator(tuple, ref accessor, Count); } IEnumerator<I> IEnumerable<I>.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } }