Skip to content

Instantly share code, notes, and snippets.

@binki
Last active February 15, 2022 20:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save binki/9e0d5f97ed8672b38de76aaa8d143f38 to your computer and use it in GitHub Desktop.
Save binki/9e0d5f97ed8672b38de76aaa8d143f38 to your computer and use it in GitHub Desktop.
An example of how to adapt IDictionary<TKey, TValue> as IReadOnlyDictionary<TKey, TValue> without incurring an object instantiation using value types
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
// Proof that this makes the compiler happy.
// See https://stackoverflow.com/a/34998637
class Program
{
static void Main(string[] args)
{
// This is written to try to demonstrate an alternative,
// type-clean way of handling http://stackoverflow.com/q/18641693/429091
var x = "blah".ToDictionary(
c => c,
c => (int)c);
// This would be where the ambiguity error would be thrown,
// but since we explicitly extend Dictionary<TKey, TValue> dirctly,
// we can write this with no issue!
x.WriteTable(Console.WriteLine, "a");
IDictionary<char, int> y = x;
y.WriteTable(Console.WriteLine, "b");
IReadOnlyDictionary<char, int> z = x;
z.WriteTable(Console.WriteLine, "lah");
}
}
// But getting compile-time type safety requires so much code duplication!
static class DictionaryExtensions
{
// Actual implementation against lowest common denominator
static void WriteTable<TDictionary, TKey, TValue>(this TDictionary dict, Action<string> writeLine, IEnumerable<TKey> keys)
where TDictionary : IReadOnlyDictionary<TKey, TValue>
{
writeLine("-");
foreach (var key in keys)
// Access values by key lookup to prove that we’re interested
// in the concept of an actual dictionary/map/lookup rather
// than being content with iterating over everything.
writeLine($"{key}:{dict[key]}");
}
// Use adapter for IReadOnlyDictionary
public static void WriteTable<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dict, Action<string> writeLine, IEnumerable<TKey> keys)
{
WriteTable<IReadOnlyDictionary<TKey, TValue>, TKey, TValue>(dict, writeLine, keys);
}
// Use façade/adapter if provided IDictionary<TKey, TValue>
public static void WriteTable<TKey, TValue>(this IDictionary<TKey, TValue> dict, Action<string> writeLine, IEnumerable<TKey> keys)
{
WriteTable<DictionaryWrapper<TKey, TValue>, TKey, TValue>(new DictionaryWrapper<TKey, TValue>(dict), writeLine, keys);
}
// Use an interface cast (a static known-safe cast).
public static void WriteTable<TKey, TValue>(this Dictionary<TKey, TValue> dict, Action<string> writeLine, IEnumerable<TKey> keys)
{
WriteTable<IReadOnlyDictionary<TKey, TValue>, TKey, TValue>(dict, writeLine, keys);
}
struct DictionaryWrapper<TKey, TValue>
: IReadOnlyDictionary<TKey, TValue>
{
readonly IDictionary<TKey, TValue> dict;
public TValue this[TKey key] => dict[key];
public int Count => dict.Count;
public IEnumerable<TKey> Keys => dict.Keys;
public IEnumerable<TValue> Values => dict.Values;
public DictionaryWrapper(IDictionary<TKey, TValue> dict)
{
this.dict = dict;
}
public bool ContainsKey(TKey key) => dict.ContainsKey(key);
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => dict.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public bool TryGetValue(TKey key, out TValue value) => dict.TryGetValue(key, out value);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment