How to create dynamic function with unsure parameters in C# Emit

173 views Asked by At

I want to create dynamic function in my project, but the number of function parameters is not sure,maybe two,maybe three or more,and then i want to run a static function already in the project,the static function accept some parameters,so,how could i do it with emit? and the most important is how to deal the parameters?

My dynamic function demo

public static void UDFCreate(string name, string type,List<string> paramList, string APIID)
{
            Type[] types = new Type[paramList.Count];
            foreach (string param in paramList)
            {
                int index = paramList.IndexOf(param);
                types[index] = typeof(double);
            }
            
            DynamicMethod mult = new DynamicMethod(name, typeof(double), types, typeof(UDFGenerator));
            ILGenerator il = mult.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);//
            il.Emit(OpCodes.Ldarg_1);// 
            il.Emit(OpCodes.Add);// 
            il.Emit(OpCodes.Ret);// 
            List<Delegate> delegates = new List<Delegate>();
            delegates.Add(mult.CreateDelegate(typeof(Func<double, double, double>)));
            // 
            ExcelFunctionAttribute att = new ExcelFunctionAttribute();
            att.Name = name;
            att.Description = "A function to multiply two numbers";
    
            List<object> funcAttribs = new List<object>();
            funcAttribs.Add(att);
            // 
            ExcelArgumentAttribute atta1 = new ExcelArgumentAttribute();
            atta1.Description = "is the first number";
    
            ExcelArgumentAttribute atta2 = new ExcelArgumentAttribute();
            atta2.Description = "is the second number";
            List<object> argAttribs = new List<object>();
            argAttribs.Add(atta1);
            argAttribs.Add(atta2);
    
            List<List<object>> argAttribsList = new List<List<object>>();
            argAttribsList.Add(argAttribs);
            try
            {
                // 
                ExcelIntegration.RegisterDelegates(delegates, funcAttribs, argAttribsList);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

My static function struct, only the paramList need create in runtime,others can get byUDFCreate

private static async void GetApiResult(string id, string type, List<string> paramNameList, params string[] paramList)

After two days later Thanks @JeremyLakeman very much,that really helpful.And then I coding like this

private static void EmitTwo(ILGenerator il, string type, List<string> paramList, string APIID)
        {
            // Stores the APIID in a local variable with index 0
            il.Emit(OpCodes.Ldarg_0); // this line is must
            il.Emit(OpCodes.Ldfld, APIID);
            il.Emit(OpCodes.Stloc_0);
            // Stores the type in a local variable with index 1
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldfld, type);
            il.Emit(OpCodes.Stloc_1);
            // Create an array of the same size as the paramList
            il.Emit(OpCodes.Ldc_I4, paramList.Count);
            il.Emit(OpCodes.Newarr, typeof(object));
            foreach (var param in paramList)
            {
                int index = paramList.IndexOf(param);
                // Replaces the value of the array with an input parameter, subscript should starts at 1
                il.Emit(OpCodes.Dup);
                il.Emit(OpCodes.Ldc_I4, index);
                il.Emit(OpCodes.Ldarg_S, index + 1);
                il.Emit(OpCodes.Stelem_Ref);
            }
            il.Emit(OpCodes.Stloc_2);
            il.Emit(OpCodes.Ldloc_0);
            il.Emit(OpCodes.Ldloc_1);
            il.Emit(OpCodes.Ldloc_2);
            il.Emit(OpCodes.Call, "GetApiResult");
            il.Emit(OpCodes.Ldstr, "LOADING");
            il.Emit(OpCodes.Ret);
        }

But it cant work fine,i got the errors in il.Emit(OpCodes.Ldfld, APIID);and in il.Emit(OpCodes.Call, "GetApiResult");.I have tried, but i dont known how to deal that,could you help to fix that? My goal is seems like thissample

1

There are 1 answers

2
Jeremy Lakeman On

Simplifying your example (from comments), you want to generate a method like;

public string test(Object a, Object b){
    print("name","type",new object[]{
        a,
        b
    });
    return "LOADING";
}

Which would result in IL similar to;

IL_0000: ldarg.0
IL_0001: ldstr "name"
IL_0006: ldstr "type"
IL_000b: ldc.i4.2 // array size
IL_000c: newarr [System.Runtime]System.Object
IL_0011: dup
IL_0012: ldc.i4.0 // array index 0
IL_0013: ldarg.1  // argument value
IL_0014: stelem.ref
IL_0015: dup
IL_0016: ldc.i4.1 // array index 1
IL_0017: ldarg.2  // argument value
IL_0018: stelem.ref
IL_0019: call instance void C::print(string, string, object[])
IL_001e: ldstr "LOADING"
IL_0023: ret

I don't have any actual experience with using il.Emit, but I would suggest you start by trying to output that method exactly. Then turn the array creation into a loop. Modifying the lines above which have comments, into the similar ldc.i4 / ldarg op codes which allow you to specify an explicit int or argument value.