Skip to content

Instantly share code, notes, and snippets.

@NickStrupat
Last active October 9, 2020 16:51
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 NickStrupat/883f1809bd56e2c9470b1c1123ea9ab6 to your computer and use it in GitHub Desktop.
Save NickStrupat/883f1809bd56e2c9470b1c1123ea9ab6 to your computer and use it in GitHub Desktop.
Dictionary to object
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