My
last post showed how to use compiled linq expressions as delegates to make a type converter. I noted that an MSIL solution would be faster. I am not a MSIL expert by any means, but here is my best attempt as using DynamicMethod and MSIL to create a delegate converter.
It does the same thing as the linq version but requires separate assign blocks for each combination of field and property.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
namespace typeoverridedemostrater
{
static class SuperConverter<T1, T2> where T2 : new()
{
private static Dictionary<Tuple<System.Type, System.Type>, MethodInvoker> converters = new Dictionary<Tuple<System.Type, System.Type>, MethodInvoker>();
delegate T2 MethodInvoker(T1 inpt);
static public T2 Convert(T1 i)
{
var key = new Tuple<System.Type, System.Type>(typeof(T2), typeof(T1));
if (!converters.ContainsKey(key))
{
DynamicMethod dm = new DynamicMethod("xmethod", typeof(T2), new Type[] { typeof(T1) }, typeof(T2));
ILGenerator il = dm.GetILGenerator();
il.DeclareLocal(typeof(T2));
il.Emit(OpCodes.Newobj, typeof(T2).GetConstructor(System.Type.EmptyTypes));
il.Emit(OpCodes.Stloc_0);
(from z in typeof(T1).GetFields().Select(a => new { name = a.Name, type = a.FieldType })
join y in typeof(T2).GetFields().Select(a => new { name = a.Name, type = a.FieldType })
on z equals y
select z.name).ToList().ForEach(a =>
{
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, typeof(T1).GetField(a));
il.Emit(OpCodes.Stfld, typeof(T2).GetField(a));
});
(from z in typeof(T1).GetProperties().Select(a => new { name = a.Name, type = a.PropertyType })
join y in typeof(T2).GetProperties().Where(b => b.CanWrite).Select(a => new { name = a.Name, type = a.PropertyType })
on z equals y
select z.name).ToList().ForEach(a =>
{
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Callvirt, typeof(T1).GetProperty(a).GetGetMethod());
il.Emit(OpCodes.Callvirt, typeof(T2).GetProperty(a).GetSetMethod());
});
(from z in typeof(T1).GetFields().Select(a => new { name = a.Name, type = a.FieldType })
join y in typeof(T2).GetProperties().Where(b => b.CanWrite).Select(a => new { name = a.Name, type = a.PropertyType })
on z equals y
select z.name).ToList().ForEach(a =>
{
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, typeof(T1).GetField(a));
il.Emit(OpCodes.Callvirt, typeof(T2).GetProperty(a).GetSetMethod());
});
(from z in typeof(T1).GetProperties().Select(a => new { name = a.Name, type = a.PropertyType })
join y in typeof(T2).GetFields().Select(a => new { name = a.Name, type = a.FieldType })
on z equals y
select z.name).ToList().ForEach(a =>
{
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Callvirt, typeof(T1).GetProperty(a).GetGetMethod());
il.Emit(OpCodes.Stfld, typeof(T2).GetField(a));
});
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ret);
var dg = (MethodInvoker)dm.CreateDelegate(typeof(MethodInvoker));
converters.Add(key, dg);
}
return (T2)converters[key](i);
}
}
}
Compared to the Linq version at 100 million repetitions (at lower numbers the results are less predictable):
Linq: 94.579 seconds vs MSIL: 93.423 seconds
Some of that time is consumed dealing with the delegates, so you can transform that into a full dynamic assembly where you can have base classes and real instance references like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Threading;
using System.Reflection;
namespace typeoverridedemostrater
{
static class SuperConverterII<T1, T2> where T2 : new()
{
private static Dictionary<Tuple<System.Type, System.Type>, convertbase<T1, T2>> converters = new Dictionary<Tuple<System.Type, System.Type>, convertbase<T1, T2>>();
static public T2 Convert(T1 i)
{
var key = new Tuple<System.Type, System.Type>(typeof(T2), typeof(T1));
if (!converters.ContainsKey(key))
{
var xassemblybuilder = Thread.GetDomain().DefineDynamicAssembly(new AssemblyName("xassembly"), System.Reflection.Emit.AssemblyBuilderAccess.Run);
var xtype = xassemblybuilder.DefineDynamicModule("xmodule").DefineType("xtype", TypeAttributes.Public, typeof(convertbase<T1, T2>));
var xfunction = xtype.DefineMethod("convert", MethodAttributes.Public | MethodAttributes.Virtual, typeof(T2), new System.Type[] { typeof(T1) });
xtype.DefineMethodOverride(xfunction, typeof(convertbase<T1, T2>).GetMethod("convert"));
var il = xfunction.GetILGenerator();
il.DeclareLocal(typeof(T2));
il.Emit(OpCodes.Newobj, typeof(T2).GetConstructor(System.Type.EmptyTypes));
il.Emit(OpCodes.Stloc_0);
(from z in typeof(T1).GetFields().Select(a => new { name = a.Name, type = a.FieldType })
join y in typeof(T2).GetFields().Select(a => new { name = a.Name, type = a.FieldType })
on z equals y
select z.name).ToList().ForEach(a =>
{
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldfld, typeof(T1).GetField(a));
il.Emit(OpCodes.Stfld, typeof(T2).GetField(a));
});
(from z in typeof(T1).GetProperties().Select(a => new { name = a.Name, type = a.PropertyType })
join y in typeof(T2).GetProperties().Where(b => b.CanWrite).Select(a => new { name = a.Name, type = a.PropertyType })
on z equals y
select z.name).ToList().ForEach(a =>
{
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Callvirt, typeof(T1).GetProperty(a).GetGetMethod());
il.Emit(OpCodes.Callvirt, typeof(T2).GetProperty(a).GetSetMethod());
});
(from z in typeof(T1).GetFields().Select(a => new { name = a.Name, type = a.FieldType })
join y in typeof(T2).GetProperties().Where(b => b.CanWrite).Select(a => new { name = a.Name, type = a.PropertyType })
on z equals y
select z.name).ToList().ForEach(a =>
{
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldfld, typeof(T1).GetField(a));
il.Emit(OpCodes.Callvirt, typeof(T2).GetProperty(a).GetSetMethod());
});
(from z in typeof(T1).GetProperties().Select(a => new { name = a.Name, type = a.PropertyType })
join y in typeof(T2).GetFields().Select(a => new { name = a.Name, type = a.FieldType })
on z equals y
select z.name).ToList().ForEach(a =>
{
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Callvirt, typeof(T1).GetProperty(a).GetGetMethod());
il.Emit(OpCodes.Stfld, typeof(T2).GetField(a));
});
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ret);
xtype.CreateType();
converters.Add(key, (convertbase<T1, T2>)xassemblybuilder.CreateInstance("xtype"));
}
return converters[key].convert(i);
}
}
public abstract class convertbase<T1, T2>
{
public abstract T2 convert(T1 i);
}
}
Compared to the original MSIL version at 100 million repetitions:
Delegate MSIL: 93.423 seconds vs Base Class MSIL: 91.814 seconds
At REALLY high volumes that is significant, and I am certain that someone better at MSIL than I could probably make it a bit faster still, but for my purposes the Linq version is more than sufficient as I find the MSIL versions a little hard to work in for the level of benefit in return.