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/6ccde6009abad6131f00b166282c7007 to your computer and use it in GitHub Desktop.
Save binki/6ccde6009abad6131f00b166282c7007 to your computer and use it in GitHub Desktop.
An example of an IDictionaryReader interface which could be used to support IDictionary<TKey, TValue> and IReadOnlyDictionary<TKey, TValue> with no transient object creation
using System;
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, IDictionaryReader<TDictionary, TKey, TValue> dictionaryReader, Action<string> writeLine, IEnumerable<TKey> keys)
{
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}:{dictionaryReader.Get(dict, key)}");
}
// Use adapter for IReadOnlyDictionary
public static void WriteTable<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dict, Action<string> writeLine, IEnumerable<TKey> keys)
{
WriteTable(dict, ReadOnlyDictionaryReader<TKey, TValue>.Instance, 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(dict, DictionaryReader<TKey, TValue>.Instance, 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)
{
dict.StaticCast<IReadOnlyDictionary<TKey, TValue>>().WriteTable(writeLine, keys);
}
// Require known compiletime-enforced static cast http://stackoverflow.com/q/3894378/429091
public static T StaticCast<T>(this T o) => o;
interface IDictionaryReader<TDictionary, TKey, TValue>
{
TValue Get(TDictionary dictionary, TKey key);
}
class DictionaryReader<TKey, TValue> : IDictionaryReader<IDictionary<TKey, TValue>, TKey, TValue>
{
public static DictionaryReader<TKey, TValue> Instance { get; } = new();
public TValue Get(IDictionary<TKey, TValue> dictionary, TKey key) => dictionary[key];
}
class ReadOnlyDictionaryReader<TKey, TValue> : IDictionaryReader<IReadOnlyDictionary<TKey, TValue>, TKey, TValue>
{
public static ReadOnlyDictionaryReader<TKey, TValue> Instance { get; } = new();
public TValue Get(IReadOnlyDictionary<TKey, TValue> dictionary, TKey key) => dictionary[key];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment