Skip to content

Instantly share code, notes, and snippets.

@stdray
Last active June 23, 2020 10:32
Show Gist options
  • Save stdray/65c391d34e02738683f364bb2fa17098 to your computer and use it in GitHub Desktop.
Save stdray/65c391d34e02738683f364bb2fa17098 to your computer and use it in GitHub Desktop.
//using static System.Linq.Expressions.Expression;
void Main()
{
var obj = new {yoba = "zheltiy", peka = 1488};
var obj2 = new {A = "zheltiy", B = 1488, C = BindingFlags.DoNotWrapExceptions};
obj.GetAnonDict().Dump();
obj2.GetAnonDict().Dump();
}
public static class AnonExt
{
static ConcurrentDictionary<Type, Func<object, Dictionary<string, object>>> getDictFuncs =
new ConcurrentDictionary<Type, Func<object, Dictionary<string, object>>>();
public static Dictionary<string, object> GetAnonDict(this object obj)
{
var tpe = obj.GetType();
if (getDictFuncs.TryGetValue(tpe, out var getDictFunc))
return getDictFunc(obj);
if (!IsAnonymousType(tpe))
return null;
var func = MakeDictFunc(tpe);
getDictFuncs[tpe] = func;
return func(obj);
}
static Func<object, Dictionary<string, object>> MakeDictFunc(Type type)
{
var flags = BindingFlags.Public | BindingFlags.Instance;
var obj = Parameter(typeof(object), "src");
var arg = Variable(type, "par");
var props = type.GetTypeInfo().GetProperties(flags)
.Where(x => x.CanRead && x.GetIndexParameters().Length == 0)
.Select(x =>
{
var key = Constant(x.Name);
var val = Property(arg, x);
return new
{
key,
val = Convert(val, typeof(object))
};
})
.ToList();
var dictType = typeof(Dictionary<string, object>);
var dict = Variable(dictType, "dict");
var dictCtor = dictType.GetConstructor(new[] {typeof(int)});
var add = dictType.GetMethod("Add", flags, null, new[] {typeof(string), typeof(object)}, null);
var body = new List<Expression>
{
Assign(arg, Convert(obj, type)),
Assign(dict, New(dictCtor, Constant(props.Count)))
};
body.AddRange(props.Select(p => Call(dict, add, p.key, p.val)));
body.Add(dict);
foreach (var expr in body)
expr.ToString().Dump();
var block = Block(new[] {arg, dict}, body);
var lambda = Lambda<Func<object, Dictionary<string, object>>>(block, obj);
return lambda.Compile();
}
static bool IsAnonymousType(Type type)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
return
type.Namespace == null
|| Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false)
&& type.IsGenericType && type.Name.Contains("AnonymousType")
&& (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$"))
&& type.Attributes.HasFlag(TypeAttributes.NotPublic);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment