Last active
February 19, 2021 21:51
-
-
Save leandromoh/09775949a9c2a07e2d1d4c600e1b423c to your computer and use it in GitHub Desktop.
Spread operator csharp
This file contains hidden or 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.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 }); | |
Console.WriteLine($"Nome = {opa.Nome}"); // Alberto | |
Console.WriteLine($"Idade = {opa.Idade}"); // 27 | |
Console.WriteLine($"Logradouro = {opa.Logradouro}"); // Rua Alberto | |
Console.WriteLine($"Numero = {opa.Numero}"); // 123 | |
Console.WriteLine($"UsaGitHub = {opa.UsaGitHub}"); // True | |
} | |
} | |
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 dynamic 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; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment