Last active
October 9, 2020 16:51
-
-
Save NickStrupat/883f1809bd56e2c9470b1c1123ea9ab6 to your computer and use it in GitHub Desktop.
Dictionary to object
This file contains 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.Concurrent; | |
using System.Collections.Generic; | |
using System.Diagnostics.CodeAnalysis; | |
using System.Linq; | |
using System.Reflection; | |
using System.Reflection.Emit; | |
public static class DictionaryObjectExtensions | |
{ | |
public static Object ToObject<TValue>(this IDictionary<String, TValue> dictionary) | |
{ | |
var type = cache.GetOrAdd(StringDictionaryKeysInfo.Create(dictionary), CreateType); | |
return Activator.CreateInstance(type, dictionary); | |
} | |
private static Type CreateType(StringDictionaryKeysInfo sdki) | |
{ | |
var ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("A"), AssemblyBuilderAccess.Run); | |
var mb = ab.DefineDynamicModule("A"); | |
var tb = mb.DefineType("A", TypeAttributes.Public); | |
var dictionaryType = typeof(IDictionary<,>).MakeGenericType(typeof(String), sdki.ValueType); | |
var cb = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new [] { dictionaryType }); | |
var ilg = cb.GetILGenerator(); | |
var fas = FieldAttributes.Public | FieldAttributes.InitOnly; | |
var tryGetValue = dictionaryType.GetMethod(nameof(IDictionary<Object, Object>.TryGetValue)); | |
foreach (var key in sdki.Keys) | |
{ | |
var fb = tb.DefineField(key, sdki.ValueType, fas); | |
ilg.Emit(OpCodes.Ldarg_1); | |
ilg.Emit(OpCodes.Ldstr, key); | |
ilg.Emit(OpCodes.Ldarg_0); | |
ilg.Emit(OpCodes.Ldflda, fb); | |
ilg.Emit(OpCodes.Callvirt, tryGetValue); | |
ilg.Emit(OpCodes.Pop); | |
} | |
ilg.Emit(OpCodes.Ret); | |
return tb.CreateType(); | |
} | |
private static readonly ConcurrentDictionary<StringDictionaryKeysInfo, Type> cache = new ConcurrentDictionary<StringDictionaryKeysInfo, Type>(); | |
internal struct StringDictionaryKeysInfo : IEquatable<StringDictionaryKeysInfo> | |
{ | |
private readonly HashSet<String> keys; | |
private readonly Int32 hashCode; | |
public Type ValueType { get; } | |
public IEnumerable<String> Keys => keys; | |
private StringDictionaryKeysInfo(Type valueType, HashSet<String> keys, Int32 hashCode) | |
{ | |
ValueType = valueType; | |
this.keys = keys; | |
this.hashCode = hashCode; | |
} | |
public static StringDictionaryKeysInfo Create<TValue>(IDictionary<String, TValue> dictionary) | |
{ | |
var keys = dictionary.Keys.ToHashSet(); | |
var hashCode = new HashCode(); | |
foreach (var key in keys) | |
hashCode.Add(key); | |
hashCode.Add(typeof(TValue)); | |
return new StringDictionaryKeysInfo(typeof(TValue), keys, hashCode.ToHashCode()); | |
} | |
public override int GetHashCode() => hashCode; | |
public override Boolean Equals(Object? o) => o is StringDictionaryKeysInfo sdk && Equals(sdk); | |
public Boolean Equals([AllowNull] StringDictionaryKeysInfo other) => ValueType == other.ValueType && keys.SetEquals(other.keys); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment