using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
class ReallyDerived : Derived
public static class Program
Dictionary<Int32,ReallyDerived> dict = new Dictionary<Int32,ReallyDerived>();
DictionaryAdapter<Int32,ReallyDerived> adapt = new DictionaryAdapter<Int32,ReallyDerived>( dict );
IReadOnlyDictionary2<Int32,ReallyDerived> orig = adapt;
IReadOnlyDictionary2<Int32,Derived > variant = adapt;
dict.Add( key: 123, value: new ReallyDerived() );
ReallyDerived reallyDerivedValue = dict[123];
Debug.Assert( variant.TryGetValue( key: 123, value: out Object? valueAsSupertype ) );
Debug.Assert( Object.ReferenceEquals( valueAsSupertype, reallyDerivedValue ) );
Console.WriteLine( "It didn't crash! ...so it worked?!" );
AcceptsAVariantReadOnlyDictionary( orig );
AcceptsAVariantReadOnlyDictionary( variant );
static void AcceptsAVariantReadOnlyDictionary( IReadOnlyDictionary2<Int32,Base> dict )
foreach( var kvp in dict )
Console.WriteLine( "dict[{0}]: \"{1}\"", kvp.Key, kvp.Value );
#region interface IReadOnlyDictionary2<TKey,out TValue>
public interface IReadOnlyDictionary2<TKey,out TValue> : IReadOnlyCollection< IPair<TKey,TValue> >
TValue? TryGetVariantValue( TKey key, out Boolean isOK );
TValue this[TKey key] { get; }
IEnumerable<TKey> Keys { get; }
IEnumerable<TValue> Values { get; }
Boolean ContainsKey( TKey key );
public interface IPair<TKey,out TValue> : ITuple
#region `class DictionaryAdapter<TKey,TValue>` + `class Dictionary2<TKey,TValue>`
public class DictionaryAdapter<TKey,TValue> : IReadOnlyDictionary2<TKey,TValue>
private readonly IReadOnlyDictionary<TKey,TValue> dict;
public DictionaryAdapter( IReadOnlyDictionary<TKey,TValue> dict )
this.dict = dict ?? throw new ArgumentNullException( nameof(dict) );
public TValue? TryGetVariantValue( TKey key, out Boolean isOK )
if( this.dict.TryGetValue( key, out TValue? value ) )
public Int32 Count => this.dict.Count;
public TValue this[TKey key] => this.dict[key];
public IEnumerable<TKey> Keys => this.dict.Keys;
public IEnumerable<TValue> Values => this.dict.Values;
public Boolean ContainsKey( TKey key ) => this.dict.ContainsKey( key );
public IEnumerator<IPair<TKey,TValue>> GetEnumerator()
using( var iter = this.dict.GetEnumerator() )
KeyValuePair<TKey,TValue> kvp = iter.Current;
yield return new PairImpl<TKey,TValue>( kvp.Key, kvp.Value );
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
public class Dictionary2<TKey,TValue> : Dictionary<TKey,TValue>, IReadOnlyDictionary2<TKey,TValue>
public TValue? TryGetVariantValue( TKey key, out Boolean isOK )
if( base.TryGetValue( key, out TValue? value ) )
public new IEnumerator<IPair<TKey,TValue>> GetEnumerator()
using( Dictionary<TKey,TValue>.Enumerator iter = base.GetEnumerator() )
KeyValuePair<TKey,TValue> kvp = iter.Current;
yield return new PairImpl<TKey,TValue>( kvp.Key, kvp.Value );
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
IEnumerable<TKey> IReadOnlyDictionary2<TKey,TValue>.Keys => base.Keys;
IEnumerable<TValue> IReadOnlyDictionary2<TKey,TValue>.Values => base.Values;
#region PairImpl and ReadOnlyDictionary2Extensions
internal sealed class PairImpl<TKey, TValue> : IPair<TKey, TValue>
public PairImpl( TKey key, TValue value )
public Object? this[int index]
if ( index == 0 ) return this.Key;
else if( index == 1 ) return this.Value;
else throw new ArgumentOutOfRangeException( paramName: nameof(index), message: "Indexer must be 0 or 1." );
public TValue Value { get; }
Int32 ITuple.Length => 2;
public static class ReadOnlyDictionary2Extensions
public static Boolean TryGetValue<TKey,TValue,TOutValue>( this IReadOnlyDictionary2<TKey,TValue> dict, TKey key, [MaybeNullWhen(false)] out TOutValue value )
TValue? gotValue = dict.TryGetVariantValue( key, isOK: out Boolean isOK );
public static IEnumerable<KeyValuePair<TKey,TValue>> AsEnumerable<TKey,TValue>( this IReadOnlyDictionary2<TKey,TValue> dict )
using( var iter = dict.GetEnumerator() )
IPair<TKey,TValue> kvp = iter.Current;
yield return new KeyValuePair<TKey,TValue>( kvp.Key, kvp.Value );
public static KeyValuePair<TKey,TValue> ToKvp<TKey,TValue>( this IPair<TKey,TValue> pair )
return new KeyValuePair<TKey,TValue>( key: pair.Key, value: pair.Value );
public static IReadOnlyDictionary2<TKey,TValue> AsReadOnly<TKey,TValue>( this Dictionary<TKey,TValue> dict )
return new DictionaryAdapter<TKey,TValue>( dict );