Skip to content

Instantly share code, notes, and snippets.

@AlbertoMonteiro
Created February 14, 2018 23:24
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save AlbertoMonteiro/f6aa8c92a7d93c9b32b4c2ab19a67def to your computer and use it in GitHub Desktop.
Save AlbertoMonteiro/f6aa8c92a7d93c9b32b4c2ab19a67def to your computer and use it in GitHub Desktop.
Spread object in C#
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
namespace Playground
{
public class Program
{
public static void Main(string[] args)
{
var cliente = new Cliente { Nome = "Alberto", Idade = 27 };
var endereco = new Endereco { Logradouro = "Rua Alberto", Numero = 123 };
var opa = cliente.Spread(endereco, new { UsaGitHub = true });
foreach (var prop in opa.GetType().GetProperties())
{
System.Console.WriteLine($"{prop.Name} = {prop.GetValue(opa)}");
}
}
}
public class Cliente
{
public string Nome { get; set; }
public short Idade { get; set; }
}
class Endereco
{
public string Logradouro { get; set; }
public int Numero { get; set; }
}
public static class Extensions
{
static Extensions()
{
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("AlbertoExcentions"), AssemblyBuilderAccess.RunAndCollect);
ModuleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
}
public static object Spread(this object obj, params object[] anotherObject)
{
var properties = anotherObject
.Concat(new[] { obj })
.SelectMany(o => o.GetType().GetProperties(), (o, p) => (o: o, p: p))
.ToDictionary(t => t.p.Name, t => (propName: t.p.Name, propType: t.p.PropertyType, propValue: t.p.GetValue(t.o)));
var objType = CreateClass(properties);
var finalObj = Activator.CreateInstance(objType);
foreach (var prop in objType.GetProperties())
prop.SetValue(finalObj, properties[prop.Name].propValue);
return finalObj;
}
const MethodAttributes METHOD_ATTRIBUTES = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
private static ModuleBuilder ModuleBuilder;
internal static Type CreateClass(IDictionary<string, (string propName, Type type, object)> parameters)
{
var typeBuilder = ModuleBuilder.DefineType(Guid.NewGuid().ToString(), TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout, null);
typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
foreach (var parameter in parameters)
CreateProperty(typeBuilder, parameter.Key, parameter.Value.type);
var type = typeBuilder.CreateTypeInfo().AsType();
return type;
}
private static PropertyBuilder CreateProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType)
{
var fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
var propBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
propBuilder.SetGetMethod(DefineGet(typeBuilder, fieldBuilder, propBuilder));
propBuilder.SetSetMethod(DefineSet(typeBuilder, fieldBuilder, propBuilder));
return propBuilder;
}
private static MethodBuilder DefineSet(TypeBuilder typeBuilder, FieldBuilder fieldBuilder, PropertyBuilder propBuilder)
=> DefineMethod(typeBuilder, $"set_{propBuilder.Name}", null, new[] { propBuilder.PropertyType }, il =>
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stfld, fieldBuilder);
il.Emit(OpCodes.Ret);
});
private static MethodBuilder DefineGet(TypeBuilder typeBuilder, FieldBuilder fieldBuilder, PropertyBuilder propBuilder)
=> DefineMethod(typeBuilder, $"get_{propBuilder.Name}", propBuilder.PropertyType, Type.EmptyTypes, il =>
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, fieldBuilder);
il.Emit(OpCodes.Ret);
});
private static MethodBuilder DefineMethod(TypeBuilder typeBuilder, string methodName, Type propertyType, Type[] parameterTypes, Action<ILGenerator> bodyWriter)
{
var methodBuilder = typeBuilder.DefineMethod(methodName, METHOD_ATTRIBUTES, propertyType, parameterTypes);
bodyWriter(methodBuilder.GetILGenerator());
return methodBuilder;
}
}
}
@leandromoh
Copy link

I did some changes to enable static type accesses to properties when type have interface implementations: link

@AlbertoMonteiro
Copy link
Author

Amazing Leandro thanks for the contribution

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment