Last active
February 15, 2022 20:38
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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