Last active
June 23, 2020 10:32
-
-
Save stdray/65c391d34e02738683f364bb2fa17098 to your computer and use it in GitHub Desktop.
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 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