DotNext by Roman Sakno

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

 ReadOnlyDictionaryView<K, I, O>

Represents lazily converted read-only dictionary.
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 ReadOnlyDictionaryView<K, I, O> : IReadOnlyDictionary<K, O>, IEnumerable<KeyValuePair<K, O>>, IEnumerable, IReadOnlyCollection<KeyValuePair<K, O>>, IEquatable<ReadOnlyDictionaryView<K, I, O>> { private readonly IReadOnlyDictionary<K, I> source; private readonly ValueFunc<I, O> mapper; public O this[K key] { get { return mapper.Invoke(source[key]); } } public IEnumerable<K> Keys => source.Keys; public IEnumerable<O> Values => Enumerable.Select<I, O>(source.Values, mapper.ToDelegate()); public int Count => source.Count; public ReadOnlyDictionaryView(IReadOnlyDictionary<K, I> dictionary, [In] [System.Runtime.CompilerServices.IsReadOnly] ref ValueFunc<I, O> mapper) { if (dictionary == null) throw new ArgumentNullException("dictionary"); source = dictionary; this.mapper = mapper; } public bool ContainsKey(K key) { return source.ContainsKey(key); } public IEnumerator<KeyValuePair<K, O>> GetEnumerator() { foreach (KeyValuePair<K, I> item in (IEnumerable<KeyValuePair<K, I>>)this.source) { Dictionary.Deconstruct<K, I>(item, out K key, out I value); K key2 = key; I arg = value; yield return new KeyValuePair<K, O>(key2, this.mapper.Invoke(arg)); } } public bool TryGetValue(K key, out O value) { if (source.TryGetValue(key, out I value2)) { value = mapper.Invoke(value2); return true; } value = default(O); return false; } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public bool Equals(ReadOnlyDictionaryView<K, 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 ReadOnlyDictionaryView<K, I, O>)) return object.Equals(source, other); ReadOnlyDictionaryView<K, I, O> other2 = (ReadOnlyDictionaryView<K, I, O>)other; return Equals(other2); } public static bool operator ==(ReadOnlyDictionaryView<K, I, O> first, ReadOnlyDictionaryView<K, I, O> second) { return first.Equals(second); } public static bool operator !=(ReadOnlyDictionaryView<K, I, O> first, ReadOnlyDictionaryView<K, I, O> second) { return !first.Equals(second); } } }