Skip to content

Instantly share code, notes, and snippets.

@loic-sharma
Last active December 15, 2016 08:30
Show Gist options
  • Save loic-sharma/ead0da76ecae9190058d7bd17781ae6a to your computer and use it in GitHub Desktop.
Save loic-sharma/ead0da76ecae9190058d7bd17781ae6a to your computer and use it in GitHub Desktop.
CLR Bytecode Generation to Get All Properties/Set All Fields from Types Unknown At Build-Time
using System;
using System.Reflection;
using System.Reflection.Emit;
namespace ConsoleApplication1
{
public class From
{
public string Hello { get; set; }
public int World { get; set; }
}
class To
{
public string Hello;
public int World;
}
class Program
{
static void Main(string[] args)
{
var getter = BuildGet(typeof(From));
var setter = BuildSet(typeof(To));
var from = getter(new From() { Hello = "Test", World = 2 });
var to = (To)setter(from);
}
static Func<object, object[]> BuildGet(Type targetType)
{
var properties = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty);
var module = typeof(Program).Module;
var returnType = typeof(object[]);
var methodArgs = new Type[] { typeof(object) };
var fetch = new DynamicMethod("get_" + targetType.Name, returnType, methodArgs, module);
var il = fetch.GetILGenerator(256); // TODO: Calculate stream size.
// Cast the input to the desired type.
var input = il.DeclareLocal(targetType);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, targetType);
il.Emit(OpCodes.Stloc, input);
// Create the result array.
il.Emit(OpCodes.Ldc_I4, properties.Length);
il.Emit(OpCodes.Newarr, typeof(object));
// Iterate through each property and set the value obtained through its get accessor
// to the result array.
for (int i = 0; i < properties.Length; ++i)
{
var getMethod = properties[i].GetGetMethod();
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4, i);
il.Emit(OpCodes.Ldloc, input);
il.Emit(OpCodes.Callvirt, getMethod);
// The result array stores values as objects, thus, we must cast primitives.
if (getMethod.ReturnType.IsValueType)
{
il.Emit(OpCodes.Box, getMethod.ReturnType);
}
il.Emit(OpCodes.Stelem_Ref);
}
// Return the result array.
il.Emit(OpCodes.Ret);
return (Func<object, object[]>)fetch.CreateDelegate(typeof(Func<object, object[]>));
}
static Func<object[], object> BuildSet(Type targetType)
{
var constructor = targetType.GetConstructor(Type.EmptyTypes);
var fields = targetType.GetFields(BindingFlags.Public | BindingFlags.Instance);
var module = typeof(Program).Module;
var returnType = typeof(object);
var methodArgs = new Type[] { typeof(object[]) };
var fetch = new DynamicMethod("set_" + targetType.Name, returnType, methodArgs, module);
var il = fetch.GetILGenerator(256); // TODO: Calculate stream size.
// var result = new TargetType();
il.Emit(OpCodes.Newobj, constructor);
// Iterate through each field and set it to its corresponding value from the input array
for (int i = 0; i < fields.Length; ++i)
{
// Load the value at this index of the input array.
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldc_I4, i);
il.Emit(OpCodes.Ldelem_Ref);
// Cast the value to the field's type.
if (fields[i].FieldType.IsValueType)
{
il.Emit(OpCodes.Unbox_Any, fields[i].FieldType);
}
else
{
il.Emit(OpCodes.Castclass, fields[i].FieldType);
}
// Now set the field to the casted value.
il.Emit(OpCodes.Stfld, fields[i]);
}
il.Emit(OpCodes.Ret);
return (Func<object[], object>)fetch.CreateDelegate(typeof(Func<object[], object>));
}
/*
static object[] Get(object input)
{
From from = (From)input;
object[] result = new object[2];
result[0] = from.Hello;
result[1] = from.World;
return result;
}
static object Set(object[] values)
{
To result = new To();
result.Hello = (string) values[0];
result.World = (int) values[1];
return result;
}
*/
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment