Dynamic code generation in C#
Let us assume that we have the following simple task:
Given a Dictionary<string, string>, convert that dictionary into a type in as performant a manner as possible. The conversion will happen many time, and first time costs are acceptable.
The answer we came up with is to dynamically generate the code based on the type. Basically, here is how it looks like:
public static Func<Dictionary<string, string>, T> Generate<T>() where T : new() { var dic = Expression.Parameter(typeof (Dictionary<string, string>), "dic"); var tmpVal = Expression.Parameter(typeof (string), "tmp"); var args = new List<MemberAssignment>(); foreach (var propertyInfo in typeof(T).GetProperties()) { var tryGet = Expression.Call(dic, "TryGetValue", new Type[0], Expression.Constant(propertyInfo.Name), tmpVal); Expression value = tmpVal; if (propertyInfo.PropertyType != typeof (string)) { var convertCall = Expression.Call(typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(Type) }), tmpVal, Expression.Constant(propertyInfo.PropertyType)); value = Expression.Convert(convertCall, propertyInfo.PropertyType); } var conditional = Expression.Condition(tryGet, value, Expression.Default(propertyInfo.PropertyType)); args.Add(Expression.Bind(propertyInfo, conditional)); } var newExpression = Expression.New(typeof(T).GetConstructor(new Type[0])); var expression = Expression.Lambda<Func<Dictionary<string, string>, T>>( Expression.Block(new[] { tmpVal },Expression.MemberInit(newExpression,args)), dic); return expression.Compile(); }
As an aside, this is an intimidating piece of code, but that is about bazillion time better than having to do this manually using IL manipulations.
What this code does is dynamically generate the following method:
(Dictionary<string,string> dic) => { string tmp; return new User { Name = dic.TryGetValue("Name", out tmp) ? tmp : default(string), Age = dic.TryGetValue("Age", out tmp) ? (int)Convert.ChangeType(tmp, typeof(int)) : default(int) }; }
This is pretty much the fastest way to do this, because this is how you would write it manually. And compiling the expression dynamically means that we don’t have to do this for each and every type we run into.
Comments
My little contribution:
http://elemarjr.net/2016/02/08/dynamic-code-generation-in-net-resposta-a-um-post-do-ayende/
Elemar, Please note that you have one issue here. The code here isn't using DLR. It is actually compiled to IL and runs at native speed :-)
Elemar, I'll admit that the IL is much more powerful, in the sense that you don't need to express everything as expression, and can use statements. If particular, something like an If is a bit hard to express
Oren,
You are right about my "DLR" mistake. I just fixed my post. I agree with you. Because of that I started a little wrapper some years ago (FuentIL [https://github.com/FluentIL/FluentIL])
Here is something you can do with FluentIL.
Oops
How do you handle nullable properties? Convert.ChangeType is a VB helper and does not work with nullables. (E.g.
Convert.ChangeType("4", typeof(int?))
)Joseph, My scenario doesn't include the need to support nullable at this time. By to support it, all you need it to provide your own method that can support it
This is a nice trick.
ChangeType is probably slow since it must be reflection based.
You could remove the need for that out parameter by writing a wrapper around TryGetValue.
I wonder whether it would make a difference to warp the algorithm around, given that you'll likely have less dictionary keys than you have fields (and that dictionary access is a little bit slower than a switch):
Victor, Interesting experiment to try. Would love to see what the results would be
Comment preview