DotNext by Roman Sakno

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

 ReadOnlyDictionaryView<K, V>

Represents read-only view of the mutable dictionary.
using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace DotNext.Collections.Generic { public readonly struct ReadOnlyDictionaryView<K, V> : IReadOnlyDictionary<K, V>, IEnumerable<KeyValuePair<K, V>>, IEnumerable, IReadOnlyCollection<KeyValuePair<K, V>>, IEquatable<ReadOnlyDictionaryView<K, V>> { private readonly IDictionary<K, V> source; public V this[K key] { get { return source[key]; } } public IEnumerable<K> Keys => source.Keys; public IEnumerable<V> Values => source.Values; public int Count => source.Count; public ReadOnlyDictionaryView(IDictionary<K, V> dictionary) { if (dictionary == null) throw new ArgumentNullException("dictionary"); source = dictionary; } public bool ContainsKey(K key) { return source.ContainsKey(key); } public IEnumerator<KeyValuePair<K, V>> GetEnumerator() { return source.GetEnumerator(); } public bool TryGetValue(K key, out V value) { return source.TryGetValue(key, out value); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public bool Equals(ReadOnlyDictionaryView<K, V> other) { return source == other.source; } public override int GetHashCode() { return RuntimeHelpers.GetHashCode(source); } public override bool Equals(object other) { if (!(other is ReadOnlyDictionaryView<K, V>)) return object.Equals(source, other); ReadOnlyDictionaryView<K, V> other2 = (ReadOnlyDictionaryView<K, V>)other; return Equals(other2); } public static bool operator ==(ReadOnlyDictionaryView<K, V> first, ReadOnlyDictionaryView<K, V> second) { return first.Equals(second); } public static bool operator !=(ReadOnlyDictionaryView<K, V> first, ReadOnlyDictionaryView<K, V> second) { return !first.Equals(second); } } }