Skip to content

Instantly share code, notes, and snippets.

@NickStrupat
Created October 31, 2018 18:59
Show Gist options
  • Save NickStrupat/e1589864c6cb7b558e41b7a384fdae4a to your computer and use it in GitHub Desktop.
Save NickStrupat/e1589864c6cb7b558e41b7a384fdae4a to your computer and use it in GitHub Desktop.
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