I'm using an object factory for dynamically creating instances by interface with help of reflection.
My code:
public static class ObjectFactory
{
private static readonly ConcurrentDictionary<Type, Type> TypeCache = new ConcurrentDictionary<Type, Type>();
public static T CreateInstance<T>(Type parent = null)
{
if (!typeof(T).IsInterface) throw new ArgumentException($"Type {typeof(T).Name} must be an interface.");
var newType = TypeCache.GetOrAdd(typeof(T), t => BuildType(typeof(T), parent));
return (T)Activator.CreateInstance(newType);
}
private static Type BuildType(Type interfaceType, Type parent = null)
{
var assemblyName = new AssemblyName($"DynamicAssembly_{Guid.NewGuid():N}");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule");
var typeName = $"{RemoveInterfacePrefix(interfaceType.Name)}_{Guid.NewGuid():N}";
var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
if (parent != null)
typeBuilder.SetParent(parent);
typeBuilder.AddInterfaceImplementation(interfaceType);
var properties = interfaceType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
var methods = interfaceType.GetMethods(BindingFlags.Instance | BindingFlags.Public);
BuildProperties(typeBuilder, properties);
BuildMethods(typeBuilder, methods);
return typeBuilder.CreateType();
string RemoveInterfacePrefix(string name) => Regex.Replace(name, "^I", string.Empty);
}
private static void BuildMethods(TypeBuilder typeBuilder, IEnumerable<MethodInfo> methods)
{
foreach (var method in methods)
{
var paramTypes = method.GetParameters().Select(p => p.ParameterType).ToArray();
AddMethodDynamically(typeBuilder, method.Name, paramTypes, method.ReturnType, "A");
}
}
private static void BuildProperties(TypeBuilder typeBuilder, IEnumerable<PropertyInfo> properties)
{
foreach (var property in properties)
{
BuildProperty(typeBuilder, property);
}
}
private static PropertyBuilder BuildProperty(TypeBuilder typeBuilder, PropertyInfo property)
{
var fieldName = $"<{property.Name}>k__BackingField";
var propertyBuilder = typeBuilder.DefineProperty(property.Name, System.Reflection.PropertyAttributes.None, property.PropertyType, Type.EmptyTypes);
// Build backing-field.
var fieldBuilder = typeBuilder.DefineField(fieldName, property.PropertyType, FieldAttributes.Private);
var getSetAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual;
var getterBuilder = BuildGetter(typeBuilder, property, fieldBuilder, getSetAttributes);
var setterBuilder = BuildSetter(typeBuilder, property, fieldBuilder, getSetAttributes);
propertyBuilder.SetGetMethod(getterBuilder);
propertyBuilder.SetSetMethod(setterBuilder);
return propertyBuilder;
}
private static MethodBuilder BuildGetter(TypeBuilder typeBuilder, PropertyInfo property, FieldBuilder fieldBuilder, MethodAttributes attributes)
{
var getterBuilder = typeBuilder.DefineMethod($"get_{property.Name}", attributes, property.PropertyType, Type.EmptyTypes);
var ilGenerator = getterBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldfld, fieldBuilder);
if (property.GetCustomAttribute<NotNullAttribute>() != null)
{
// Build null check
ilGenerator.Emit(OpCodes.Dup);
var isFieldNull = ilGenerator.DefineLabel();
ilGenerator.Emit(OpCodes.Brtrue_S, isFieldNull);
ilGenerator.Emit(OpCodes.Pop);
ilGenerator.Emit(OpCodes.Ldstr, $"{property.Name} isn't set.");
var invalidOperationExceptionConstructor = typeof(InvalidOperationException).GetConstructor(new Type[] { typeof(string) });
ilGenerator.Emit(OpCodes.Newobj, invalidOperationExceptionConstructor);
ilGenerator.Emit(OpCodes.Throw);
ilGenerator.MarkLabel(isFieldNull);
}
ilGenerator.Emit(OpCodes.Ret);
return getterBuilder;
}
private static void AddMethodDynamically(TypeBuilder myTypeBld,
string mthdName,
Type[] mthdParams,
Type returnType,
string mthdAction)
{
MethodBuilder myMthdBld = myTypeBld.DefineMethod(
mthdName,
MethodAttributes.Public,
returnType,
mthdParams);
ILGenerator ILout = myMthdBld.GetILGenerator();
int numParams = mthdParams.Length;
for (byte x = 0; x < numParams; x++)
{
ILout.Emit(OpCodes.Ldarg_S, x);
}
if (numParams > 1)
{
for (int y = 0; y < (numParams - 1); y++)
{
switch (mthdAction)
{
case "A":
ILout.Emit(OpCodes.Add);
break;
case "M":
ILout.Emit(OpCodes.Mul);
break;
default:
ILout.Emit(OpCodes.Add);
break;
}
}
}
ILout.Emit(OpCodes.Ret);
}
private static MethodBuilder BuildSetter(TypeBuilder typeBuilder, PropertyInfo property, FieldBuilder fieldBuilder, MethodAttributes attributes)
{
var setterBuilder = typeBuilder.DefineMethod($"set_{property.Name}", attributes, null, new Type[] { property.PropertyType });
var ilGenerator = setterBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldarg_1);
// Build null check
if (property.GetCustomAttribute<NotNullAttribute>() != null)
{
var isValueNull = ilGenerator.DefineLabel();
ilGenerator.Emit(OpCodes.Dup);
ilGenerator.Emit(OpCodes.Brtrue_S, isValueNull);
ilGenerator.Emit(OpCodes.Pop);
ilGenerator.Emit(OpCodes.Ldstr, property.Name);
var argumentNullExceptionConstructor = typeof(ArgumentNullException).GetConstructor(new Type[] { typeof(string) });
ilGenerator.Emit(OpCodes.Newobj, argumentNullExceptionConstructor);
ilGenerator.Emit(OpCodes.Throw);
ilGenerator.MarkLabel(isValueNull);
}
ilGenerator.Emit(OpCodes.Stfld, fieldBuilder);
ilGenerator.Emit(OpCodes.Ret);
return setterBuilder;
}
}
Method BuildMethods isn't creating implementation method by interface.
Please help solve 2 problems:
Create implementation method by interface;
All created classes have parent classes and all methods on interface after implementation have only executed method by parent class/ for example
public class Parent
{
public T Get<T, Y>(string url, params object[] query) => httpClient.Get<T>(url, query);
public T Post<T, Y>(string url, params Y model) => httpClient.Post<T>(url, model);
}
Create interface:
public inteface IRest
{
[HttpGet("/api/user-ids")] List<string> GetUserIds(int count, int skip);
}
What I want in finally on create instance by interface, I got instance where method GetUserIds execute Get<T, Y> from parent class
var rest = ObjectFactory.CreateInstance<IRest>(typeof(Parent));
var userIds = rest.GetUserIds(10, 0);
Related
I am trying to build dynamic types, inherited from a base object, based on a definition object (fieldnames/types) and then apply (potentially multiple) interfaces to that.
The base type has an object array to store values. The getters and setters use a generic method in the base type to save a specific type to an array location.
The "CreateType" works, but when I try to instantiate the object, I get:
System.InvalidProgramException : Common Language Runtime detected an invalid program.
I can't tell where the failure occurs.
Here is the Object definition:
public class UTObjectDef : IUTObjectDef
{
List<UTFieldDef> fieldDefs = new List<UTFieldDef>();
Dictionary<string, Type> interfaces = new Dictionary<string,Type>();
List<string> nameMap = new List<String>();
public string Name {get; set;}
public IUTFieldDef[] FieldDefs {
get {
return fieldDefs.ToArray();
}
}
public Type[] Interfaces {
get {
return interfaces.Values.ToArray();
}
}
public UTObjectDef(string name) {
Name = name;
}
public UTObjectDef(Type fromInterface) {
PropertyInfo[] props = fromInterface.GetProperties();
Name = fromInterface.Name;
if (Name.ToUpper().StartsWith("I")) {
Name = Name.Substring(1);
}
foreach( PropertyInfo prop in props) {
AddField(prop.Name, prop.PropertyType);
}
interfaces.Add(Name, fromInterface);
}
public void AddField(string name, Type fieldType)
{
var fieldDef = new UTFieldDef(name, fieldType);
fieldDefs.Add(fieldDef);
nameMap.Add(name);
}
public int FieldIndex(string fieldName) {
return nameMap.IndexOf(fieldName);
}
}
And the field definition:
public class UTFieldDef : IUTFieldDef
{
public string FieldName {get; set;}
public Type FieldType {get; set;}
public int? Length {get; set;}
public int? Decimals {get; set;}
public UTFieldDef(string fieldName, Type fieldType) {
FieldName = fieldName;
FieldType = fieldType;
}
}
The base type (Note: The constructor sets the arrays to the right size based on the field count), and the static UTObjectDef is populated through an Initialize static method, added in the TypeBuilder):
public class UTObject : IUTObject{
protected Object?[] values = new Object[0];
protected Object?[] origValues = new Object[0];
public T? getValue<T>(int index) {
return (T?)values[index];
}
public void setValue<T>(int index, T? value) {
values[index] = value;
}
}
And the dynamic object construction:
public static void buildTypes() {
MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
AssemblyBuilder ab = AssemblyBuilder.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Run);
ModuleBuilder mb = ab.DefineDynamicModule(aName.Name);
Type baseType = typeof(UTObject.UTObject);
Type objDefType = typeof(UTObjectDef);
var valuesField = baseType.GetField("values", BindingFlags.NonPublic | BindingFlags.Instance);
var origValuesField = baseType.GetField("origValues", BindingFlags.NonPublic | BindingFlags.Instance);
foreach (UTObjectDef oDef in objectDefs.Values) {
TypeBuilder tb = mb.DefineType( oDef.Name, TypeAttributes.Public, baseType);
// Create a static field to hold the value.
const string FieldName = "ObjectDef";
var field = tb.DefineField(
FieldName,
objDefType,
FieldAttributes.Static | FieldAttributes.InitOnly | FieldAttributes.Public);
// Create a method to initialize the field.
const string InitializeMethodName = "Initialize";
var initMethod = tb.DefineMethod(
InitializeMethodName,
MethodAttributes.Static | MethodAttributes.Private,
CallingConventions.Standard,
typeof(void),
new[] { objDefType });
var il = initMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Stsfld, field);
il.Emit(OpCodes.Ret);
System.Type[] param = {};
Type invalOp = typeof(InvalidOperationException);
ConstructorInfo invOpExInfo = invalOp.GetConstructor(
Type.EmptyTypes
);
ConstructorInfo baseCon = baseType.GetConstructor(Type.EmptyTypes);
ConstructorBuilder ctor = tb.DefineConstructor( MethodAttributes.Public, CallingConventions.Standard, param );
ILGenerator cil = ctor.GetILGenerator();
Label isInit = cil.DefineLabel(); // define label to jump in case condition is false
MethodInfo getFldDefsInfo = objDefType.GetMethod("get_FieldDefs");
cil.Emit(OpCodes.Ldarg_0);
cil.Emit(OpCodes.Call, baseCon );
cil.Emit(OpCodes.Ldsfld, field);
cil.Emit(OpCodes.Ldnull);
cil.Emit(OpCodes.Ceq);
cil.Emit(OpCodes.Brfalse, isInit);
cil.Emit(OpCodes.Nop);
cil.Emit(OpCodes.Ldstr, "Not Initialized");
cil.Emit(OpCodes.Newobj, invOpExInfo);
cil.ThrowException(invalOp);
cil.MarkLabel(isInit);
cil.Emit(OpCodes.Nop);
cil.Emit(OpCodes.Ldarg_0);
cil.Emit(OpCodes.Call, getFldDefsInfo);
cil.Emit(OpCodes.Ldlen);
cil.Emit(OpCodes.Conv_I4);
cil.Emit(OpCodes.Newarr, typeof(System.Object));
cil.Emit(OpCodes.Stfld, valuesField);
cil.Emit(OpCodes.Ldarg_0);
cil.Emit(OpCodes.Call, getFldDefsInfo);
cil.Emit(OpCodes.Ldlen);
cil.Emit(OpCodes.Conv_I4);
cil.Emit(OpCodes.Newarr, typeof(System.Object));
cil.Emit(OpCodes.Stfld, origValuesField);
cil.Emit(OpCodes.Ret);
MethodInfo[] methods = typeof(UTObject.UTObject).GetMethods();
MethodInfo? getMethod = null;
MethodInfo? setMethod = null;
foreach(MethodInfo mi in methods) {
if (mi.Name.Contains("getValue") && mi.IsGenericMethod) {
getMethod = mi;
}
if (mi.Name.Contains("setValue") && mi.IsGenericMethod) {
setMethod = mi;
}
}
for (int i = 0; i < oDef.FieldDefs.Length; i++ )
{
IUTFieldDef fDef = oDef.FieldDefs[i];
PropertyBuilder fldPropBldr = tb.DefineProperty(fDef.FieldName,
PropertyAttributes.None,
fDef.FieldType,
null);
MethodBuilder getMethodBuilder = tb.DefineMethod("get_" + fDef.FieldName,
getSetAttr,
fDef.FieldType,
Type.EmptyTypes);
if (getMethod != null) {
ILGenerator GetPropGetIL = getMethodBuilder.GetILGenerator();
LocalBuilder fldIdx = GetPropGetIL.DeclareLocal(typeof(Int32));
MethodInfo getMethodInfo = getMethod.MakeGenericMethod(fDef.FieldType);
GetPropGetIL.Emit(OpCodes.Ldc_I4, i);
GetPropGetIL.Emit(OpCodes.Stloc, fldIdx);
GetPropGetIL.Emit(OpCodes.Ldarg_0);
GetPropGetIL.Emit(OpCodes.Ldloc, fldIdx);
GetPropGetIL.Emit(OpCodes.Call, getMethodInfo);
GetPropGetIL.Emit(OpCodes.Ret);
}
MethodBuilder setMethodBuilder = tb.DefineMethod("set_" + fDef.FieldName,
getSetAttr,
null,
new Type[] { fDef.FieldType });
if (setMethod != null) {
ILGenerator SetPropGetIL = setMethodBuilder.GetILGenerator();
LocalBuilder fldIdx = SetPropGetIL.DeclareLocal(typeof(Int32));
SetPropGetIL.Emit(OpCodes.Ldc_I4, i);
SetPropGetIL.Emit(OpCodes.Stloc, fldIdx);
SetPropGetIL.Emit(OpCodes.Ldarg_0);
SetPropGetIL.Emit(OpCodes.Ldloc, fldIdx);
SetPropGetIL.Emit(OpCodes.Call, setMethod.MakeGenericMethod(fDef.FieldType));
SetPropGetIL.Emit(OpCodes.Nop);
SetPropGetIL.Emit(OpCodes.Ret);
}
fldPropBldr.SetGetMethod(getMethodBuilder);
fldPropBldr.SetSetMethod(setMethodBuilder);
}
/* foreach(Type interfaceType in oDef.Interfaces) {
tb.AddInterfaceImplementation(interfaceType);
}
*/
Type? newType = tb.CreateType();
// Invoke the initializer method using reflection, passing the provided value to initialize the new field.
MethodInfo initMethodInfo = newType.GetMethod(InitializeMethodName, BindingFlags.Static | BindingFlags.NonPublic);
if (initMethodInfo != null) {
initMethodInfo.Invoke(null, new object[] { oDef });
}
foreach(Type interfaceType in oDef.Interfaces) {
objectTypesByInterface.Add(interfaceType.Name, newType);
}
}
I want to implement an interface at runtime, however when i execute
return Activator.CreateInstance(typeBuilder.CreateTypeInfo().AsType(), new RPCRequestProxy());
in RPCClientFactory.cs it throws
System.TypeLoadException: Signature of the body and declaration in a
method implementation do not match.
I use dotnet core 1.0.4.
The entry point Program.cs :
namespace RPC
{
interface IRCPClient
{
Task Foo([UrlParam]int b, [UrlParam]string a, [UrlParam] DateTime c);
}
class Program
{
static void Main(string[] args)
{
var factory = new RPCClientFactory();
factory.Register<IRCPClient>();
var cli = factory.GetClient<IRCPClient>();
cli.Foo(1, "HelloWorld", DateTime.Now);
Console.ReadKey();
}
}
}
The RPCClientFactory.cs:
public class RPCClientFactory
{
private readonly ConcurrentDictionary<Type, object> _clients = new ConcurrentDictionary<Type, object>();
public void Register<ClientType>()
{
Register(typeof(ClientType));
}
public void Register(Type type)
{
if (!_clients.TryAdd(type, CreateImplInstance(type)))
throw new InvalidOperationException($"bad type{type}");
}
public object CreateImplInstance(Type type)
{
var typeBuilder = CreateTypeBuilder(type);
var methods = type.GetMethods();
var field = CreateFiled(typeBuilder);
CreateCotr(typeBuilder, field);
CreateMethods(typeBuilder, methods, field);
typeBuilder.AddInterfaceImplementation(type);
return Activator.CreateInstance(typeBuilder.CreateTypeInfo().AsType(), new RPCRequestProxy());
}
private static TypeBuilder CreateTypeBuilder(Type type)
{
var typeSignature = "MyDynamicType";
var an = new AssemblyName(typeSignature);
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(Guid.NewGuid().ToString()), AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout,
null);
return tb;
}
private static FieldInfo CreateFiled(TypeBuilder typeBuilder)
{
return typeBuilder.DefineField("_proxy", typeof(RPCRequestProxy), FieldAttributes.Private);
}
private static void CreateCotr(TypeBuilder typeBuilder, FieldInfo proxyField)
{
var ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public
, CallingConventions.Standard
, new[] { typeof(RPCRequestProxy) });
var il = ctorBuilder.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0]));
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stfld, proxyField);
il.Emit(OpCodes.Ret);
}
public void CreateMethods(TypeBuilder typeBuilder, MethodInfo[] methods, FieldInfo field)
{
foreach (var method in methods)
{
CreateMethod(typeBuilder, method, field);
}
}
private static void CreateMethod(TypeBuilder typeBuilder, MethodInfo method, FieldInfo proxyFiled)
{
var paramters = method.GetParameters();
var methodBuilder = typeBuilder.DefineMethod(method.Name,
MethodAttributes.Public,
CallingConventions.Standard,
method.ReturnType,
paramters.Select(x => x.GetType()).ToArray());
var il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Newobj, typeof(Dictionary<string, object>).GetConstructor(new Type[0]));
il.Emit(OpCodes.Stloc_0);
for (var i = 0; i < paramters.Length; ++i)
{
var param = paramters[i];
if (param.GetCustomAttribute<BodyParamAttribute>() != null) continue;
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldstr, param.Name);
il.Emit(OpCodes.Ldarg, i + 1);
if (param.ParameterType.GetTypeInfo().IsValueType)
{
il.Emit(OpCodes.Box, param.GetType());
}
il.Emit(OpCodes.Callvirt, typeof(Dictionary<string, object>).GetMethod("Add"));
}
var proxyMethod = typeof(RPCRequestProxy)
.GetMethods()
.First(x => x.Name == "PostAsync" && x.GetParameters().Length == 2);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, proxyFiled);
il.Emit(OpCodes.Ldstr, "xxx.xxx.xxx");
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Callvirt, proxyMethod);
il.Emit(OpCodes.Stloc_1);
il.Emit(OpCodes.Ldloc_1);
il.Emit(OpCodes.Ret);
typeBuilder.DefineMethodOverride(methodBuilder, method);
}
public ClientType GetClient<ClientType>() where ClientType : class
{
_clients.TryGetValue(typeof(ClientType), out object cli);
return cli as ClientType;
}
public ClientType GetRequiredClient<ClientType>() where ClientType : class
{
var cli = GetClient<ClientType>();
if (cli == null) throw new InvalidOperationException($"bad type{typeof(ClientType)}");
return cli;
}
}
The RPCRequestProxy.cs:
public class RPCRequestProxy
{
public RPCRequestProxy()
{
}
public async Task PostAsync(string url, IDictionary<string, object> urlParams)
{
await Task.Run(() =>
{
Console.WriteLine("URL:" + url);
Console.WriteLine("QueryString:");
foreach (var kv in urlParams)
{
Console.WriteLine($"{kv.Key}={kv.Value}");
}
});
}
}
After register IRCPClient, i hope it generates a class like this:
class RCPClient : IRCPClient
{
private readonly RPCRequestProxy _proxy;
public RCPClient(RPCRequestProxy proxy)
{
_proxy = proxy;
}
public Task Foo([UrlParam] int b, [UrlParam] string a, [UrlParam] DateTime c)
{
var urlParams = new Dictionary<string, object>();
urlParams.Add(nameof(b), b);
urlParams.Add(nameof(a), a);
urlParams.Add(nameof(c), c);
return _proxy.PostAsync("xxx.xxx.xxx", urlParams);
}
}
Finally, i worked it out:
1: We must DeclareLocal before we use a local value.
2: Change paramters.Select(x => x.GetType()).ToArray() to paramters.Select(x => x.ParameterType).ToArray()
This is my final CreateMethod method of RPCClientFactory.cs
private static void CreateMethod(TypeBuilder typeBuilder, MethodInfo method, FieldInfo proxyFiled)
{
var paramters = method.GetParameters();
var methodBuilder = typeBuilder.DefineMethod(method.Name,
MethodAttributes.Public
| MethodAttributes.Virtual
| MethodAttributes.HideBySig
| MethodAttributes.SpecialName,
CallingConventions.Standard,
method.ReturnType,
paramters.Select(x => x.ParameterType).ToArray()); // change this line
var il = methodBuilder.GetILGenerator();
il.DeclareLocal(typeof(Dictionary<string, object>)); // add this line
il.Emit(OpCodes.Newobj, typeof(Dictionary<string, object>).GetConstructor(new Type[0]));
il.Emit(OpCodes.Stloc_0);
for (short i = 0; i < paramters.Length; ++i)
{
var param = paramters[i];
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldstr, param.Name);
il.Emit(OpCodes.Ldarg, (short)(i +1));
if (param.ParameterType.GetTypeInfo().IsValueType)
{
il.Emit(OpCodes.Box, param.ParameterType);
}
il.Emit(OpCodes.Callvirt, typeof(Dictionary<string, object>).GetMethod("Add"));
}
var proxyMethod = typeof(RPCRequestProxy)
.GetMethods()
.First(x => x.Name == "PostAsync" && x.GetParameters().Length == 2);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, proxyFiled);
il.Emit(OpCodes.Ldstr, "xxx.xxx.xxx");
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Callvirt, proxyMethod);
il.Emit(OpCodes.Ret);
}
I want to know how to generator the following method myMethod with ilGenerator.
public class MyClass {
private MyField myField;
public int myMethod(int b) {
return myField.someMethod(b);
}
}
Can someone help me ?
class Program
{
static MethodAttributes attrs = MethodAttributes.Public | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
static Type CreateType() {
var myDomain = Thread.GetDomain();
var myAsmName = new AssemblyName { Name = "Demo" };
var myAsmBuilder = myDomain.DefineDynamicAssembly(myAsmName, AssemblyBuilderAccess.RunAndSave);
var moudle = myAsmBuilder.DefineDynamicModule("DemoModule", "Demo.dll");
var typeBuilder = moudle.DefineType("MyClass", TypeAttributes.Public | TypeAttributes.BeforeFieldInit);
var fieldBuilder = typeBuilder.DefineField("myField", typeof(MyField), FieldAttributes.Private);
var ctorBuilder = typeBuilder.DefineConstructor(attrs, CallingConventions.HasThis | CallingConventions.Standard, Type.EmptyTypes);
var methodBuilder = typeBuilder.DefineMethod("MyMethod", MethodAttributes.Public | MethodAttributes.HideBySig, typeof(int), new[] { typeof(int) });
var ctorILGen = ctorBuilder.GetILGenerator();
var ilGen = methodBuilder.GetILGenerator();
var someMethod = fieldBuilder.FieldType.GetMethod("SomeMethod");
ctorILGen.Emit(OpCodes.Ldarg_0);
ctorILGen.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
ctorILGen.Emit(OpCodes.Ldarg_0);
ctorILGen.Emit(OpCodes.Newobj, fieldBuilder.FieldType.GetConstructor(Type.EmptyTypes));
ctorILGen.Emit(OpCodes.Stfld, fieldBuilder);
ctorILGen.Emit(OpCodes.Ret);
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Ldfld, fieldBuilder);
ilGen.Emit(OpCodes.Ldarg_1);
ilGen.Emit(OpCodes.Callvirt, someMethod);
ilGen.Emit(OpCodes.Ret);
return typeBuilder.CreateType();
}
static void Main(string[] args) {
var type = CreateType();
dynamic instance = Activator.CreateInstance(type);
Console.WriteLine(instance.MyMethod(10));
}
}
will generate
public class MyClass
{
private MyField myField;
public MyClass() {
myField = new MyField();
}
public int myMethod(int b) {
return myField.SomeMethod(b);
}
}
public class MyField
{
public int SomeMethod(int i) {
return i;
}
}
I have the following code which dynamically generates an assembly and classes based on an EdmModel. This is all working fine, however I a bit confused on how I can update the assembly when the model changes.
Here are my current thoughts, either:
Update the current classes, adding/removing properties. Adding new classes when needed.
Create another version of the assembly, allowing me to have the two versions running at the same time (This approach is preferable as it would allow the user to access the different versions of the model, and would mean the current version could still be used while the new one was being generated)
Is it possible to run two versions of the same assembly this way? Is it possible to update a type that has already been created? Am I doing this right?
public class DynamicAssembyGenerator
{
private IDictionary<string, AssemblyBuilder> _assemblyBuilders;
private IDictionary<string, ModuleBuilder> _moduleBuilders;
private IDictionary<string, TypeBuilder> _typeBuilders;
private IDictionary<string, EnumBuilder> _enumBuilders;
private IDictionary<string, Type> _types;
public void Create(IEdmModel model)
{
_assemblyBuilders = model.DeclaredNamespaces.ToDictionary(declaredNamespace => declaredNamespace,
CreateAssemblyBuilder);
_moduleBuilders = model.DeclaredNamespaces.ToDictionary(declaredNamespace => declaredNamespace,
declaredNamespace => CreateModuleBuilder(declaredNamespace, _assemblyBuilders[declaredNamespace]));
_typeBuilders = new Dictionary<string, TypeBuilder>();
_enumBuilders = new Dictionary<string, EnumBuilder>();
_types = new Dictionary<string, Type>();
foreach (
var edmSchemaElement in
model.SchemaElements.Where(s => s.SchemaElementKind == EdmSchemaElementKind.TypeDefinition))
{
if (_moduleBuilders.ContainsKey(edmSchemaElement.Namespace))
{
CreateType(model, edmSchemaElement.FullName(), _moduleBuilders[edmSchemaElement.Namespace]);
}
}
foreach (var assemblyBuilder in _assemblyBuilders.Values)
{
assemblyBuilder.Save(assemblyBuilder.GetName().Name + ".dll");
}
foreach (var value in _types.Values)
{
var edmType = model.FindDeclaredType(value.FullName);
model.SetAnnotationValue<ClrTypeAnnotation>(edmType, new ClrTypeAnnotation(value));
}
}
private void CreateType(IEdmModel model, string typeName, ModuleBuilder moduleBuilder)
{
if (_typeBuilders.ContainsKey(typeName) || _enumBuilders.ContainsKey(typeName))
{
return;
}
var structuredType = model.FindDeclaredType(typeName) as IEdmStructuredType;
if (structuredType != null)
{
var entityType = structuredType as IEdmEntityType;
var typeBuilder = CreateTypeBuilder(structuredType, model, moduleBuilder);
_typeBuilders.Add(typeName, typeBuilder);
var constructorBuilder = CreateConstructor(typeBuilder);
ILGenerator il = constructorBuilder.GetILGenerator();
foreach (var edmProperty in structuredType.DeclaredProperties)
{
CreateProperty(edmProperty, model, typeBuilder, il,
entityType != null && entityType.HasDeclaredKeyProperty(edmProperty));
}
il.Emit(OpCodes.Ret);
_types.Add(typeName, typeBuilder.CreateType());
return;
}
var enumType = model.FindDeclaredType(typeName) as IEdmEnumType;
if (enumType != null)
{
var enumBuilder = CreateEnumBuilder(typeName, moduleBuilder);
_enumBuilders.Add(typeName, enumBuilder);
foreach (var edmEnumMember in enumType.Members)
{
var value = edmEnumMember.Value as EdmIntegerConstant;
if (value != null)
{
CreateEnumValue(edmEnumMember.Name, Convert.ToInt32(value.Value), enumBuilder);
}
}
_types.Add(typeName, enumBuilder.CreateType());
return;
}
}
private AssemblyBuilder CreateAssemblyBuilder(string assemblyName)
{
var appDomain = AppDomain.CurrentDomain;
var assemblyBuilder = appDomain.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.RunAndSave);
return assemblyBuilder;
}
private ModuleBuilder CreateModuleBuilder(string assemblyName, AssemblyBuilder assemblyBuilder)
{
var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName, assemblyName + ".dll");
return moduleBuilder;
}
private TypeBuilder CreateTypeBuilder(IEdmStructuredType type, IEdmModel model, ModuleBuilder moduleBuilder)
{
var baseType = typeof(DynamicEntityBase);
if (type.BaseType != null)
{
var propertyType = EdmLibHelpers.GetClrType(type.BaseType, model);
if (propertyType == null)
{
var schemaElement = model.FindDeclaredType(type.BaseType.FullTypeName());
CreateType(model, schemaElement.FullName(), _moduleBuilders[schemaElement.Namespace]);
baseType = _types[schemaElement.FullName()];
}
}
return moduleBuilder.DefineType(type.FullTypeName(), TypeAttributes.Class | TypeAttributes.Public, baseType);
}
private EnumBuilder CreateEnumBuilder(string typeName, ModuleBuilder moduleBuilder)
{
return moduleBuilder.DefineEnum(typeName, TypeAttributes.Public, typeof(int));
}
private void CreateEnumValue(string name, int value, EnumBuilder enumBuilder)
{
enumBuilder.DefineLiteral(name, value);
}
private void CreateProperty(IEdmProperty edmProperty, IEdmModel model, TypeBuilder typeBuilder, ILGenerator constructorIlGenerator, bool isKey)
{
var propertyType = GetPropertyType(edmProperty, model);
var propertyName = edmProperty.Name;
FieldBuilder fFirst;
PropertyBuilder pFirst = CreateProperty(propertyName, propertyType, typeBuilder, out fFirst);
SetDefaultValue(edmProperty, model, fFirst, constructorIlGenerator);
if (isKey)
{
SetPropertyCustomAttribute(pFirst, typeof(KeyAttribute));
}
if (edmProperty.VocabularyAnnotations(model).Any(v => v.Term.FullName() == CoreVocabularyConstants.Computed))
{
SetPropertyCustomAttribute(pFirst, typeof(ComputedAttribute));
}
if (edmProperty.PropertyKind == EdmPropertyKind.Navigation)
{
if (edmProperty.Type.IsCollection())
{
CreateProperty(propertyName + "Ids", typeof(IList<string>), typeBuilder, out fFirst);
var listType = typeof(List<string>);
constructorIlGenerator.Emit(OpCodes.Ldarg_0);
constructorIlGenerator.Emit(OpCodes.Newobj, listType.GetConstructor(new Type[0]));
constructorIlGenerator.Emit(OpCodes.Stfld, fFirst);
}
else
{
CreateProperty(propertyName + "Id", typeof(string), typeBuilder, out fFirst);
}
}
}
private static PropertyBuilder CreateProperty(string propertyName, Type propertyType, TypeBuilder typeBuilder, out FieldBuilder fieldBuilder)
{
fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
PropertyBuilder pFirst = typeBuilder.DefineProperty(propertyName,
PropertyAttributes.HasDefault, propertyType, null);
//Getter
MethodBuilder mFirstGet = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
ILGenerator firstGetIL = mFirstGet.GetILGenerator();
firstGetIL.Emit(OpCodes.Ldarg_0);
firstGetIL.Emit(OpCodes.Ldfld, fieldBuilder);
firstGetIL.Emit(OpCodes.Ret);
//Setter
MethodBuilder mFirstSet = typeBuilder.DefineMethod("set_" + propertyName, MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig, null, new Type[] { propertyType });
ILGenerator firstSetIL = mFirstSet.GetILGenerator();
firstSetIL.Emit(OpCodes.Ldarg_0);
firstSetIL.Emit(OpCodes.Ldarg_1);
firstSetIL.Emit(OpCodes.Stfld, fieldBuilder);
firstSetIL.Emit(OpCodes.Ret);
pFirst.SetGetMethod(mFirstGet);
pFirst.SetSetMethod(mFirstSet);
return pFirst;
}
private static void SetPropertyCustomAttribute(PropertyBuilder propertyBuilder, Type attributeType)
{
var attributeBuilder = new CustomAttributeBuilder(attributeType.GetConstructor(new Type[0]),
new object[0]);
propertyBuilder.SetCustomAttribute(attributeBuilder);
}
private Type GetPropertyType(IEdmProperty edmProperty, IEdmModel model)
{
var edmPropertyType = GetEdmType(edmProperty.Type);
var propertyType = EdmLibHelpers.GetClrType(edmPropertyType, model);
if (propertyType == null)
{
var schemaElement = model.FindDeclaredType(edmPropertyType.Definition.FullTypeName());
CreateType(model, schemaElement.FullName(), _moduleBuilders[schemaElement.Namespace]);
propertyType = _typeBuilders[schemaElement.FullName()];
}
if (edmProperty.Type.IsCollection())
{
var listType = typeof(IList<>);
propertyType = listType.MakeGenericType(propertyType);
}
return propertyType;
}
private IEdmTypeReference GetEdmType(IEdmTypeReference type)
{
if (type.IsCollection())
{
var collectionType = type.AsCollection();
return collectionType.CollectionDefinition().ElementType;
}
else
{
return type; ;
}
}
private void SetDefaultValue(IEdmProperty property, IEdmModel model, FieldBuilder builder, ILGenerator constructorIlGenerator)
{
if (!property.Type.IsNullable)
{
var propertyType = GetPropertyType(property, model);
if (property.Type.IsComplex())
{
constructorIlGenerator.Emit(OpCodes.Ldarg_0);
constructorIlGenerator.Emit(OpCodes.Newobj, propertyType.GetConstructor(new Type[0]));
constructorIlGenerator.Emit(OpCodes.Stfld, builder);
}
else if (property.Type.IsCollection())
{
var listType = typeof(List<>);
var collectionType = listType.MakeGenericType(propertyType.GetGenericArguments()[0]);
constructorIlGenerator.Emit(OpCodes.Ldarg_0);
constructorIlGenerator.Emit(OpCodes.Newobj, collectionType.GetConstructor(new Type[0]));
constructorIlGenerator.Emit(OpCodes.Stfld, builder);
}
}
}
private ConstructorBuilder CreateConstructor(TypeBuilder typeBuilder)
{
ConstructorBuilder constructor = typeBuilder.DefineConstructor(
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.RTSpecialName,
CallingConventions.Standard,
new Type[0]);
return constructor;
}
}
Update the current classes, adding/removing properties.
Not possible. Once you call the TypeBuilder.CreateType() the type you created can't be removed/modified.
Adding new classes when needed.
Possible without problems. You can use the types you define in your AssemblyBuilder without saving it, so it is possible to add new types.
Create another version of the assembly, allowing me to have the two versions running at the same time
No problem here.
Is it possible to run two versions of the same assembly this way?
It wouldn't be the same assembly for .NET... What do they have in common? Your abstract concept that "they are the same"? But the .NET can't look into your brain :-)
I am using Code Emit for dynamic code generation.
I would like to set a field using an external factory method.
Here is my (reduced) code:
Defines:
Func<object> fact = () => new B();
var mi = fact.GetMethodInfo();
var t = typeof(B);
Emit code:
ILGenerator ilg;
var tb = _mb.DefineType("myProxy", TypeAttributes.Public | TypeAttributes.Class, typeof(object));
var fieldBuilder = tb.DefineField("proxy", t, FieldAttributes.Private);
var ctorBuilder = tb.DefineConstructor(...);
ilg = ctorBuilder.GetILGenerator();
ilg.Emit(OpCodes.Ldarg_0);
ilg.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
ilg.Emit(OpCodes.Ldarg_0);
ilg.Emit(OpCodes.Callvirt, mi);
ilg.Emit(OpCodes.Castclass, t);
ilg.Emit(OpCodes.Stfld, fieldBuilder);
ilg.Emit(OpCodes.Ret);
Create an instace:
Activator.CreateInstance(tb.CreateType());
TargetInvocationException is thrown
{"Method not found: \"?\"."}
Here is what I am looking forward to generate:
public class A
{
private B _proxy;
public A(Func<object> factory)
{
_proxy = (B)factory();
}
}
BUT the factory Method is fixed and not provided as parameter...
public class A
{
private B _proxy;
public A()
{
_proxy = (B) //[GENERATE ME] () => new B();
}
}
Any Suggestions?
You have to invoke delegate via "Invoke" method but you need to give factory delegate to your proxy. You can add a parameter to your proxy constructor.
public class B
{
}
static internal class Metadata<T> //Avoid lock & string metadata description
{
static public readonly Type Type = typeof(T);
static public FieldInfo Field<X>(Expression<Func<T, X>> expression)
{
return (expression.Body as MemberExpression).Member as FieldInfo;
}
static public PropertyInfo Property<X>(Expression<Func<T, X>> expression)
{
return (expression.Body as MemberExpression).Member as PropertyInfo;
}
static public MethodInfo Method(Expression<Action<T>> expression)
{
return (expression.Body as MethodCallExpression).Method;
}
static public MethodInfo Method<X>(Expression<Func<T, X>> expression)
{
return (expression.Body as MethodCallExpression).Method;
}
}
class Program
{
static void Main(string[] args)
{
var _Factory = new Func<object>(() => new B());
TypeBuilder _TypeBuilder = null;// = ...;
var _Parameters = new Type[] { Metadata<Func<object>>.Type };
var _Constructor = _TypeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, _Parameters);
var _Body = _Constructor.GetILGenerator();
//...
_Body.Emit(OpCodes.Ldarg_1);
_Body.Emit(OpCodes.Call, Metadata<Func<object>>.Method(_Func => _Func.Invoke()));
//...
var _Type = _TypeBuilder.CreateType();
var _Parameter = Expression.Parameter(Metadata<Func<object>>.Type);
var _New = Expression.Lambda<Func<Func<object>, object>>(Expression.New(_Type.GetConstructor(_Parameters), _Parameter), _Parameter).Compile();
var _Instance = _New(_Factory);
}
}