0

The below code works only for calling methods without reference parameters.

public delegate void testD2(params object[] args);

public static testD2 SetTestD2(MethodInfo method)
{
  ParameterExpression param = Expression.Parameter(typeof(object[]), "args");
  ParameterInfo[] paramsInfo = method.GetParameters();
  Expression[] argsExp = new Expression[paramsInfo.Length];
  for (int i = 0; i < paramsInfo.Length; i++)
  {
    Expression index = Expression.Constant(i);
    Type paramType = paramsInfo[i].ParameterType;

    //??? ByRef removed from type, becuse Expression.Call with ByRef parametrs lead to compile error
    if (paramType.IsByRef == true) 
      paramType = paramType.GetElementType();
    //??? and for this reason is not change of parameters permanent

    Expression paramAccessorExp = Expression.ArrayIndex(param, index);
    Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType);
    argsExp[i] = paramCastExp;
  }

  var blockExp = Expression.Call(method, argsExp);

  LambdaExpression result = Expression.Lambda(typeof(testD2), blockExp, param); //change in param?
  return (testD2)result.Compile();
}

I am thinking to change the input parameters passed with params keyword, or create new params for lambda, but I don't know how.

  public class testCls
  {
    public void Test()
    {
      MethodInfo mi = typeof(XXX).GetMethod("TestMethod");
      var compiledObject2 = XXX.SetTestD2(mi);

      int k = 5;
      compiledObject2(k); //k is not passed as ByRef
    }
  }

public static void TestMethod(ref int a)
{
  a = a * a;
}
2
  • Welcome to Stack Overflow. What's the bigger picture here? What are you trying to accomplish with expression trees? Do you have to use ref parameters, or could you return the appropriate values from the method? Do you have to use that delegate type, or could you use one that actually matches the method you're trying to call? Without more background, it's going to be hard to provide an answer. Commented Dec 22, 2019 at 9:41
  • Is it possible that it was in fact passed by ref? Since you're using params object[] I'm assuming that the int (5) gets boxed and put in the array. That boxed value is now independant from the original value. If that boxed value is passed by ref, the underlying value of k won't change. That's why I think it might actually be passed by ref and you just won't notice it since it's now boxed in the object[] and the original value isn't connected to that. Just one idea I had, I really don't know if that's a possibility. Commented Dec 22, 2019 at 12:39

2 Answers 2

1

ref or out parameters are incompatible with params, I think that's a fundamental language or runtime limitation.

To do what you want, you can compile into strongly-typed delegate of the correct type. Expression.GetDelegateType call can create you that correct type; unlike params object[] there can be output and ref parameters.

Sign up to request clarification or add additional context in comments.

Comments

0

Strongly typed delegate is a way, but requires extra code for various methods. That is why I tried for a general function to create general delegates. To the keyword params, I think if the function is called classically DelegateSomeFunc (a, b, c,), then nothing is passed by reference. But if the function is called by a array, then parameters are passed by reference.

  int k = 5;
  string b = "aa";
  object[] objArr = { k, b };

  compiledObject2(k, b);   //parameters passed by value
  compiledObject2(objArr); //array passed by reference (params don't need create new array?)

Delegate call for example this function

  public static void TestMethod(ref int a, string text)
  {
    a = a * a;
  }

Theoretically is possible create delegate with function, but there is another problem - call function with array of exspressions parametrs rather then with array of expressions. Code above in original post have a row

var blockExp = Expression.Call(method, argsExp);

But argsExp probably cannot return parameters changed by a function. For this reason i write input parameters in local expression variables, which called function can change and finally put the changed values in input parameter array.

public static Class1.testD2 SetTestD2(System.Reflection.MethodInfo method)
{
  ParameterExpression param = Expression.Parameter(typeof(object[]), "args"); // Expression.Parameter(typeof(object[]), "args");
  BinaryExpression[] byExp=null;
  Expression[] argsExp = GetArgExp(method.GetParameters(), param, ref byExp);

  ParameterExpression xxx = Expression.Variable(typeof(int));
  ParameterExpression yyy = Expression.Variable(typeof(string));

  var blockExp =
      Expression.Block( new[] { xxx, yyy } //variables
      , Expression.Assign(xxx, argsExp[0]) 
      , Expression.Assign(yyy, argsExp[1])
      , Expression.Call(method, xxx, yyy)
      , Expression.Assign(Expression.ArrayAccess(param, Expression.Constant(0)), Expression.Convert(xxx, typeof(object))) //change input param array
      ) ;

  LambdaExpression result = Expression.Lambda(typeof(testD2), blockExp, param); 
  return (testD2)result.Compile();
}

The function is just an example for changing the input parameter. Now is possible return changed parameter.

  MethodInfo mi = typeof(Class1).GetMethod("TestMethod");
  var compiledObject2 = Class1.SetTestD2(mi);
  int k = 5;
  string b = "aa";
  object[] objArr = { k, b };

  compiledObject2(k, b);   //no changes
  compiledObject2(objArr); //objArr[0] changed

But I do not know if function what create delegate can be modified to create a general delegate.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.