DotNext by Roman Sakno

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

 ReadOnlyListView<I, O>

Represents lazily converted read-only list.
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace DotNext.Collections.Generic { [StructLayout(LayoutKind.Auto)] public readonly struct ReadOnlyListView<I, O> : IReadOnlyList<O>, IEnumerable<O>, IEnumerable, IReadOnlyCollection<O>, IEquatable<ReadOnlyListView<I, O>> { private readonly IReadOnlyList<I> source; private readonly ValueFunc<I, O> mapper; public O this[int index] { get { return mapper.Invoke(source[index]); } } public int Count => source.Count; public ReadOnlyListView(IReadOnlyList<I> list, [In] [System.Runtime.CompilerServices.IsReadOnly] ref ValueFunc<I, O> mapper) { if (list == null) throw new ArgumentNullException("list"); source = list; this.mapper = mapper; } public IEnumerator<O> GetEnumerator() { return Enumerable.Select<I, O>((IEnumerable<I>)source, mapper.ToDelegate()).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public bool Equals(ReadOnlyListView<I, O> other) { if (source == other.source) return ref mapper == ref other.mapper; return false; } public override int GetHashCode() { return RuntimeHelpers.GetHashCode(source) ^ mapper.GetHashCode(); } public override bool Equals(object other) { if (!(other is ReadOnlyListView<I, O>)) return object.Equals(source, other); ReadOnlyListView<I, O> other2 = (ReadOnlyListView<I, O>)other; return Equals(other2); } public static bool operator ==(ReadOnlyListView<I, O> first, ReadOnlyListView<I, O> second) { return first.Equals(second); } public static bool operator !=(ReadOnlyListView<I, O> first, ReadOnlyListView<I, O> second) { return !first.Equals(second); } } }