Last active
December 15, 2016 08:30
-
-
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
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 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