Created
October 31, 2018 18:59
-
-
Save NickStrupat/e1589864c6cb7b558e41b7a384fdae4a to your computer and use it in GitHub Desktop.
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.Collections.Generic; | |
using System.Linq; | |
using System.Reflection; | |
using System.Reflection.Emit; | |
namespace NickStrupat | |
{ | |
public static class ModuleBuilderExtensions | |
{ | |
public static TypeBuilder DefineDerivedType<TBase>(this ModuleBuilder moduleBuilder, String name) => moduleBuilder.DefineDerivedType(name, typeof(TBase)); | |
public static TypeBuilder DefineDerivedType(this ModuleBuilder moduleBuilder, String name, Type baseType, Boolean copyNonInheritedAttributes = false) | |
{ | |
var typeBuilder = moduleBuilder.DefineType(name, baseType.Attributes, baseType, baseType.GetInterfaces()); | |
if (copyNonInheritedAttributes) | |
foreach (var baseTypeCustomAttribute in baseType.CustomAttributes.Where(attribute => attribute.AttributeType.GetCustomAttribute<AttributeUsageAttribute>()?.Inherited != true)) | |
typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(baseTypeCustomAttribute.Constructor, baseTypeCustomAttribute.ConstructorArguments.Select(x => x.Value).ToArray())); | |
return typeBuilder; | |
} | |
} | |
public static class TypeBuilderExtensions | |
{ | |
public static TypeBuilder DefineContructorsFromBase(this TypeBuilder typeBuilder) | |
{ | |
const BindingFlags bindingFlags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; | |
var baseConstructors = typeBuilder.BaseType.GetConstructors(bindingFlags).Where(x => !x.IsPrivate); | |
typeBuilder.DefineContructorsFromBaseInternal(baseConstructors); | |
return typeBuilder; | |
} | |
public static TypeBuilder DefineContructorsFromBase(this TypeBuilder typeBuilder, IList<ConstructorInfo> baseConstructors) | |
{ | |
foreach (var baseConstructor in baseConstructors) | |
if (typeBuilder.BaseType != baseConstructor.DeclaringType) | |
throw new ArgumentException($"All `{nameof(ConstructorInfo)}`s must be declared on the base type of this `{nameof(TypeBuilder)}`", nameof(baseConstructors)); | |
else if (baseConstructor.IsPrivate) | |
throw new ArgumentException($"All `{nameof(ConstructorInfo)}`s must not be private", nameof(baseConstructors)); | |
return typeBuilder.DefineContructorsFromBaseInternal(baseConstructors); | |
} | |
private static TypeBuilder DefineContructorsFromBaseInternal(this TypeBuilder typeBuilder, IEnumerable<ConstructorInfo> baseConstructors) | |
{ | |
foreach (var baseConstructor in baseConstructors) | |
{ | |
var constructorParameterTypes = baseConstructor.GetParameters().Select(x => x.ParameterType).ToArray(); | |
var constructorBuilder = typeBuilder.DefineConstructor(baseConstructor.Attributes, CallingConventions.Standard, constructorParameterTypes); | |
var cbilg = constructorBuilder.GetILGenerator(); | |
cbilg.Emit(OpCodes.Ldarg_0); | |
for (var i = 0; i < constructorParameterTypes.Length; i++) | |
cbilg.Emit(OpCodes.Ldarg, i + 1); | |
cbilg.Emit(OpCodes.Call, baseConstructor); | |
cbilg.Emit(OpCodes.Ret); | |
} | |
return typeBuilder; | |
} | |
private const MethodAttributes methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName; | |
public static TypeBuilder DefineAutoProperty(this TypeBuilder typeBuilder, Type type, String name) => | |
typeBuilder.DefineAutoProperty(type, name, methodAttributes); | |
public static TypeBuilder DefineAutoProperty(this TypeBuilder typeBuilder, Type type, String name, MethodAttributes getterAndSetterAttributes) => | |
typeBuilder.DefineAutoProperty(type, name, getterAndSetterAttributes, getterAndSetterAttributes, null); | |
public static TypeBuilder DefineAutoProperty(this TypeBuilder typeBuilder, Type type, String name, IEnumerable<Type> attributes) => | |
typeBuilder.DefineAutoProperty(type, name, methodAttributes, attributes); | |
public static TypeBuilder DefineAutoProperty(this TypeBuilder typeBuilder, Type type, String name, MethodAttributes getterAndSetterAttributes, IEnumerable<Type> attributes) => | |
typeBuilder.DefineAutoProperty(type, name, getterAndSetterAttributes, getterAndSetterAttributes, attributes.Select(x => (x.GetConstructor(Type.EmptyTypes), (Object[])null)).ToList()); | |
public static TypeBuilder DefineAutoProperty(this TypeBuilder typeBuilder, Type type, String name, | |
IList<(ConstructorInfo constructor, Object[] arguments)> attributes) => | |
typeBuilder.DefineAutoProperty(type, name, methodAttributes, attributes); | |
public static TypeBuilder DefineAutoProperty(this TypeBuilder typeBuilder, Type type, String name, MethodAttributes getterAndSetterAttributes, | |
IList<(ConstructorInfo constructor, Object[] arguments)> attributes) => | |
typeBuilder.DefineAutoProperty(type, name, getterAndSetterAttributes, getterAndSetterAttributes, attributes); | |
public static TypeBuilder DefineAutoProperty( | |
this TypeBuilder typeBuilder, Type type, String name, | |
MethodAttributes getterAttributes, MethodAttributes setterAttributes, | |
IList<(ConstructorInfo constructor, Object[] arguments)> attributes) | |
{ | |
var fieldBuilder = typeBuilder.DefineField("_" + Char.ToLower(name[0]) + name.Skip(1), type, FieldAttributes.Private); | |
var propertyBuilder = typeBuilder.DefineProperty(name, PropertyAttributes.None, type, null); | |
if (attributes != null) | |
foreach (var (constructor, arguments) in attributes) | |
propertyBuilder.SetCustomAttribute(new CustomAttributeBuilder(constructor, arguments ?? Array.Empty<Object>())); | |
var mbIdGetAccessor = typeBuilder.DefineMethod($"get_{name}", getterAttributes, type, Type.EmptyTypes); | |
var getIl = mbIdGetAccessor.GetILGenerator(); | |
getIl.Emit(OpCodes.Ldarg_0); | |
getIl.Emit(OpCodes.Ldfld, fieldBuilder); | |
getIl.Emit(OpCodes.Ret); | |
var mbIdSetAccessor = typeBuilder.DefineMethod($"set_{name}", setterAttributes, null, new[] { type }); | |
var setIl = mbIdSetAccessor.GetILGenerator(); | |
setIl.Emit(OpCodes.Ldarg_0); | |
setIl.Emit(OpCodes.Ldarg_1); | |
setIl.Emit(OpCodes.Stfld, fieldBuilder); | |
setIl.Emit(OpCodes.Ret); | |
propertyBuilder.SetGetMethod(mbIdGetAccessor); | |
propertyBuilder.SetSetMethod(mbIdSetAccessor); | |
return typeBuilder; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment