Last active
February 15, 2022 20:38
-
-
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
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; | |
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