DotNext by Roman Sakno

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

 ReadOnlyCollectionView<I, O>

Represents lazily converted read-only collection.
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace DotNext.Collections.Generic { public readonly struct ReadOnlyCollectionView<I, O> : IReadOnlyCollection<O>, IEnumerable<O>, IEnumerable, IEquatable<ReadOnlyCollectionView<I, O>> { private readonly IReadOnlyCollection<I> source; private readonly ValueFunc<I, O> mapper; public int Count => source.Count; public ReadOnlyCollectionView(IReadOnlyCollection<I> collection, [In] [System.Runtime.CompilerServices.IsReadOnly] ref ValueFunc<I, O> mapper) { if (collection == null) throw new ArgumentNullException("collection"); source = collection; 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(ReadOnlyCollectionView<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 ReadOnlyCollectionView<I, O>)) return object.Equals(source, other); ReadOnlyCollectionView<I, O> other2 = (ReadOnlyCollectionView<I, O>)other; return Equals(other2); } public static bool operator ==(ReadOnlyCollectionView<I, O> first, ReadOnlyCollectionView<I, O> second) { return first.Equals(second); } public static bool operator !=(ReadOnlyCollectionView<I, O> first, ReadOnlyCollectionView<I, O> second) { return !first.Equals(second); } } }