How to call a method on a field with ILGenerator.Emit? - c#

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;
}
}

Related

PropertyBuilder Get/Set with Generic Method and a values array

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);
}
}

How to implement interface method with permanent body?

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);

Signature of the body and declaration in a method implementation do not match

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);
}

Properly emit property

I'm writing an interface implementator for simple interfaces like
interface IPoint
{
int X { get; }
int Y { get; }
}
It almost works, but when I try to implement any property I get an error
Signature of the body and declaration in a method implementation do not match
I don't understand why Emit thinks that properties does not match.
Here is code sample:
private static class InterfaceImplementator<T> where T: class
{
[SuppressMessage("ReSharper", "StaticMemberInGenericType")]
public static Type Value { get; }
static InterfaceImplementator()
{
var interfaceType = typeof(T);
if (!interfaceType.IsInterface)
{
throw new ArgumentException($"{interfaceType.FullName} should be an interface!");
}
var interfaceProps = interfaceType.GetProperties();
if (interfaceType.GetMethods().Except(interfaceProps.Select(x => x.GetMethod).Concat(interfaceProps.Select(x => x.SetMethod))).Any())
{
throw new ArgumentException($"{interfaceType.FullName} must have properties only!");
}
var tb = Builder.DefineType($"<{interfaceType.Name}>__Implementation", TypeAttributes.Class | TypeAttributes.Sealed);
foreach (var interfaceProp in interfaceProps)
{
var prop = tb.EmitAutoProperty(interfaceProp.Name, interfaceProp.PropertyType);
if (interfaceProp.CanRead)
{
tb.DefineMethodOverride(prop.GetMethod, interfaceProp.GetMethod);
}
if (interfaceProp.CanWrite)
{
tb.DefineMethodOverride(prop.SetMethod, interfaceProp.SetMethod);
}
}
tb.AddInterfaceImplementation(interfaceType);
Value = tb.CreateType();
}
}
where EmitProperty:
public static PropertyInfo EmitAutoProperty(this TypeBuilder tb, string propertyName, Type propertyType)
{
var backingField = tb.DefineField($"<{propertyName}>k__BackingField", propertyType, FieldAttributes.Private);
var propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
var getMethod = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig);
var getGenerator = getMethod.GetILGenerator();
getGenerator.Emit(OpCodes.Ldarg_0);
getGenerator.Emit(OpCodes.Ldfld, backingField);
getGenerator.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getMethod);
var setMethod = tb.DefineMethod("set_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig);
var setGenerator = setMethod.GetILGenerator();
setGenerator.Emit(OpCodes.Ldarg_0);
setGenerator.Emit(OpCodes.Ldarg_1);
setGenerator.Emit(OpCodes.Stfld, backingField);
setGenerator.Emit(OpCodes.Ret);
propertyBuilder.SetSetMethod(setMethod);
return propertyBuilder;
}
Try using the 4-arg DefineMethod call for the get_ and set_ methods so you can define the return type / arg:
var getMethod = tb.DefineMethod("get_" + propertyName,
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
propertyType,
Type.EmptyTypes);
var setMethod = tb.DefineMethod("set_" + propertyName,
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
null,
new [] { propertyType });

MethodBuilder ILGenerator get class property

I'm trying to acces the property of the class my Methodbuilder method is being defined in.
This is my current code:
Type[] types = { typeof(HttpListenerContext) };
TypeBuilder tb = GetTypeBuilder(type.Name);
ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
MethodBuilder mB = tb.DefineMethod("Init", MethodAttributes.Public | MethodAttributes.Virtual, null, types);
ILGenerator il = mB.GetILGenerator();
il.Emit(OpCodes.Ldstr, typeof(Page).GetProperty("_POST").GetValue(??));
il.Emit(OpCodes.Call, typeof(DataSet).GetMethod("SetData"));
il.Emit(OpCodes.Ret);
tb.DefineMethodOverride(mB, typeof(Page).GetMethod("Init"));
try
{
Type tc = tb.CreateType();
Page test = (Page)Activator.CreateInstance(tc);
test.Init();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
This is is class I'm trying to get the property from:
public class Page
{
public Dictionary<string, string> _POST { get; set; }
public Dictionary<string, string> _GET { get; set; }
public Head Headers { get; set; }
public string ContentType { get; set; }
public int StatusCode = 200;
public virtual void Init(HttpListenerContext ctx = null) { }
public virtual void Load() { }
public virtual string Send() { return ""; }
public virtual string Send(string response) { return ""; }
}
The current typebuilder has Page as parent, how can I get a nonstatic value set in my typebuilder class? Like how could I get my method to Console.WriteLine the value of _POST?
private TypeBuilder GetTypeBuilder(string name)
{
string typeSignature = name;
AssemblyName aN = new AssemblyName(typeSignature);
AssemblyBuilder aB = AppDomain.CurrentDomain.DefineDynamicAssembly(aN, AssemblyBuilderAccess.Run);
ModuleBuilder mB = aB.DefineDynamicModule("MainModule");
TypeBuilder tB = mB.DefineType(typeSignature + "h",
TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout,
typeof(Page));
return tB;
}
ILGenerator il = mB.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldstr, typeof(Page).GetProperty("ContentType").GetGetMethod());
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
il.Emit(OpCodes.Ret);
tb.DefineMethodOverride(mB, typeof(Page).GetMethod("Init"));
I'm setting the value of ContentType before I'm getting it with my function.
It seems like you're creating an instance method on a type that inherits from Page. If that's the case, this is the code you want:
il.Emit(OpCodes.Ldarg_0); // Load the this reference
il.Emit(OpCodes.Call, typeof(Page).GetProperty("_POST").GetGetMethod());
Also, note that DataSet doesn't have a SetData method - if that's an extension method, you need to use the real type where it's defined rather than DataSet.

Categories

Resources