C# lambda delegate call methods with reference variables - c#

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

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.

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.

Related

Using System.Linq.Expressions to span a tuple in an array of object [C#]

Let's suppose I've a method like this
public static int Sum(int a, int b)
{
return a + b;
}
I need to construct an expression so that the new method must be returned in the form new object[1]{Sum(a, b);} and to do so I use this code:
var callMethod = Expression.Call(mi, parameters);
// new object[] { Sum((a, b) }
var returnResult = Expression.NewArrayInit(
typeof(object),
Expression.Convert(callMethod, typeof(object)));
where mi is the Sum method taken via reflection.
Now, I need to extend the generation of the expression to manage a tuple as output paramert and the result of the tuple must be spanned into the result array.. Pratically, what I would like to do is converting this method
public static (int result, bool isOverflowed) Sum(int a, int b)
{
return (a + b, false);
}
into this:
var callMethod = Expression.Call(mi, parameters);
// new object[] { Sum((a, b) }
var returnResult = Expression.NewArrayInit(
typeof(object),
Expression.Convert(Tuple1, typeof(object)), // The desired
Expression.Convert(Tuple2, typeof(object))); // The desired
How this can be accomplished?
Since the method will return ValueTuple<int, bool>, it is possible to get the values from Item1 and Item2 fields. The basic idea is to assign the method return to a variable and get Item1 and Item2 fields.
var mi = typeof(Program).GetMethod("Sum");
var parameters = new ParameterExpression[]{ Expression.Parameter(typeof(int)), Expression.Parameter(typeof(int)) };
var callMethod = Expression.Call(mi, parameters);
var variable = Expression.Variable(typeof(ValueTuple<int, bool>));
var assign = Expression.Assign(variable, callMethod);
var returnResult = Expression.NewArrayInit(typeof(object),
Expression.Convert(Expression.Field(variable, "Item1"), typeof(object)),
Expression.Convert(Expression.Field(variable, "Item2"), typeof(object)));
var block = Expression.Block(new ParameterExpression[] { variable }, assign, returnResult);
var x = Expression.Lambda(block, parameters).Compile().DynamicInvoke(1 , 2);

C# - Creating lambda functions using expression trees of an arbitrary delegate type

I am trying to create a runtime lambda function of an arbitrary type, that collects the arguments, that were supplied to it, in a list of objects and passes them to another method of type void Method(List<object> list) to process them. I wrote this code, but got really confused with the result:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace LambdaTest
{
class LambdaCreator
{
ParameterExpression[] Parameters;
int Index = 0;
public ParameterExpression Next()
{
return Parameters[Index++];
}
public void ResetIndex()
{
Index = 0;
}
public void Bar(List<object> parameters)
{
foreach (var p in parameters)
{
PrintType(p);
}
}
public void PrintType(object arg)
{
Console.WriteLine(arg.GetType().Name);
}
public T CreateLambda<T>() where T : class
{
var barMethod = GetType().GetMethod("Bar");
Parameters = typeof(T).GetMethod("Invoke")
.GetParameters()
.Select(x => Expression.Parameter(x.ParameterType))
.ToArray();
var parametersCount = Expression.Constant(Parameters.Length);
var listType = typeof(List<object>);
var list = Expression.Variable(listType);
var index = Expression.Variable(typeof(int));
var thisObject = Expression.Constant(this);
var resetIndex = GetType().GetMethod("ResetIndex");
var next = GetType().GetMethod("Next");
var printType = GetType().GetMethod("PrintType");
var add = listType.GetMethod("Add");
var breakLabel = Expression.Label();
var block = Expression.Block(
new ParameterExpression[] { list, index },
Expression.Call(thisObject, printType, Parameters.FirstOrDefault()),
Expression.Call(thisObject, resetIndex),
Expression.Assign(list, Expression.New(listType)),
Expression.Loop(
Expression.Block(
Expression.IfThen(Expression.GreaterThanOrEqual(index, parametersCount), Expression.Break(breakLabel)),
Expression.Call(list, add, Expression.Call(thisObject, next)),
Expression.AddAssign(index, Expression.Constant(1))
),
breakLabel
),
Expression.Call(thisObject, barMethod, list)
);
var lambda = Expression.Lambda(typeof(T), block, Parameters);
var compiledLambda = lambda.Compile() as T;
return compiledLambda;
}
}
class Program
{
delegate void Foo(string a, int b);
static void Main(string[] args)
{
var test = new LambdaCreator();
var l = test.CreateLambda<Foo>();
l("one", 2);
}
}
}
The output of the program was:
String
PrimitiveParameterExpression`1
PrimitiveParameterExpression`1
I was expecting to get:
String
String
Int32
Somehow I lose the values of the arguments when I put them in a list and pass it to the Bar method.
Can someone tell me where is the problem I how can I fix it. Or is there another way to collect the arguments and pass them through? I'm really new to this expression trees stuff. Thanks in advance!
You can create a NewArrayExpression using the Parameters array while constructing the lambda function, prior to the Expression.Loop block, and modify the calling code to access the array, like so:
// Declare a paramArray parameter to use inside the Expression.Block
var paramArray = Expression.Parameter(typeof(object[]), "paramArray");
var block = Expression.Block(
new ParameterExpression[] { list, index, paramArray }, // pass in paramArray here
Expression.Call(thisObject, printType, Parameters.FirstOrDefault()),
Expression.Call(thisObject, resetIndex),
Expression.Assign(list, Expression.New(listType)),
/* Assign the array - make sure to box value types using Expression.Convert */
Expression.Assign(
paramArray,
Expression.NewArrayInit(
typeof(object),
Parameters.Select(p => Expression.Convert(p, typeof(object))))),
Expression.Loop(
Expression.Block(
Expression.IfThen(Expression.GreaterThanOrEqual(index, parametersCount), Expression.Break(breakLabel)),
//Expression.Call(list, add, Expression.Call(thisObject, next)),
Expression.Call(list, add, Expression.ArrayIndex(paramArray, index)), // use the paramArray here
Expression.AddAssign(index, Expression.Constant(1))
),
breakLabel
),
Expression.Call(thisObject, barMethod, list)
);
The rest is unchanged - this code replaces the var block = ... statement entirely. Works as you specified.
This happens because this call:
Expression.Call(thisObject, printType, Parameters.FirstOrDefault())
Is actually compiled to something like:
this.PrintType(a)
Where a is your delegate parameter, while this:
Expression.Call(list, add, Expression.Call(thisObject, next))
Is compiled to something like:
this.PrintType(this.Next())
One of the options would be to modify print method:
public void PrintType(object arg)
{
if(arg is ParameterExpression expr)
{
Console.WriteLine(expr.Type.Name);
}
else
{
Console.WriteLine(arg.GetType().Name);
}
}
To fill list you can just create an corresponding expression:
var list = Expression.Variable(listType);
var exprs = new List<Expression>
{
Expression.Call(thisObject, resetIndex),
Expression.Assign(list, Expression.New(listType)),
};
for (int i = 0; i < #params.Length; i++)
{
var ex = Expression.Call(list, add, Expression.Convert(#params[i], typeof(object)));
exprs.Add(ex);
}
exprs.Add(Expression.Call(thisObject, barMethod, list));
var block = Expression.Block(new[] {list}, exprs);
Or use var property = Expression.PropertyOrField(thisObject, nameof(Parameters)); (with changing Parameters to List<object>, assigning new list to it and deleting block parameters) instead of list.

Linq Expression referenced from scope but its not defined

I am trying to create a Action that uses two parameters, one of the parameters are the class instance and the other is a object array. The object array types are unknown as I find the methods with a attribute, so I gave the below a try but the expr.Compile() throws an error
variable 'arg1[0]' of type 'System.Object' referenced from scope '', but it is not defined
public static T BuildDelegate<T>(MethodInfo method)
{
var dgtMi = typeof(T).GetMethod("Invoke");
var dgtParams = dgtMi.GetParameters();
var preDeterminedParams = new ParameterExpression[2]
{
Expression.Parameter(dgtParams[0].ParameterType, "arg0"),
Expression.Parameter(typeof(object[]), "arg1")
};
var methodParams = method.GetParameters();
var paramThis = Expression.Convert(preDeterminedParams[0], method.DeclaringType);
var paramsToPass = CreateParam(methodParams);
var expr = Expression.Lambda<T>(
Expression.Call(paramThis, method, paramsToPass),
preDeterminedParams);
return expr.Compile();
}
private static Expression[] CreateParam(ParameterInfo[] parameters)
{
var expressions = new Expression[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
{
//Trying to create a placeholder for any objects that might be passed through arg1
expressions[i] = Expression.Convert(
Expression.Parameter(typeof(object), "arg1[" + i + "]"),
parameters[i].ParameterType);
}
return expressions;
}
expr looks like this before trying to compile
(arg0, arg1) => Convert(arg0, Test).TestingInvocation(Convert(arg1[0], String), Convert(arg1[1], Int32), Convert(arg1[2], Single)
I am guessing since arg1[0] was created in CreateParam() as a place holder it does not have a reference value. Not sure how to create a holder, so it can have some sort of reference.
Essentially I am trying to accomplish this after the expression is compiled
Action<T(anyclass reference), object[] (unknown params)>
(arg0, arg1)=>{ Convert(arg0, T).Method(Convert(arg1[0], ToUnknownType))}
In the following snippet,
//...
//Trying to create a placeholder for any objects that might be passed through arg1
expressions[i] = Expression.Convert(
Expression.Parameter(typeof(object), "arg1[" + i + "]"),
parameters[i].ParameterType);
//...
You are basically doing arg1[0] => ... which is not a valid expression.
You are most likely looking for Expression.Array* related calls in order to access the array index.
public static T BuildDelegate<T>(MethodInfo method) {
var dgtMi = typeof(T).GetMethod("Invoke");
var dgtParams = dgtMi.GetParameters();
var preDeterminedParams = new ParameterExpression[2] {
Expression.Parameter(dgtParams[0].ParameterType, "arg0"),
Expression.Parameter(typeof(object[]), "arg1")
};
ParameterInfo[] methodParams = method.GetParameters();
var paramThis = Expression.Convert(preDeterminedParams[0], method.DeclaringType);
// arg1 =>
var arg1 = preDeterminedParams[1];
// arg1 => Convert(arg1[0], SomeType), Convert(arg1[1], SomeType), ....
var paramsToPass = CreateParam(arg1, methodParams);
var expr = Expression.Lambda<T>(
Expression.Call(paramThis, method, paramsToPass),
preDeterminedParams);
return expr.Compile();
}
private static Expression[] CreateParam(ParameterExpression arg1, ParameterInfo[] parameters) {
var expressions = new Expression[parameters.Length];
for (int i = 0; i < parameters.Length; i++) {
//arg1 => Convert(arg1[i], SomeType)
expressions[i] = Expression.Convert(
Expression.ArrayIndex(arg1, Expression.Constant(i)), parameters[i].ParameterType
);
}
return expressions;
}

Extending Func to accept array parameter in C#

I have been using Func to create
Expression.Lambda<Func<object, object>>(block, paramValues).Compile();
Where block is Expression.Block that creates the required execution plan. paramValues is an array of Expression.Parameter values:
var paramValues = epDef.Value.Parameters.Select(p => Expression.Parameter(typeof(object), p.Name))
.ToArray();
I am trying to modify this to accept an array, so that I can use Func when I have more than 16 input parameters:
Expression.Lambda<Func<object[], object>>(block, paramValues).Compile();
However this gives me the following error:
Incorrect number of parameters supplied for lambda declaration
Can anyone help me with using Func to accept more than 16 parameters in this case? either using an array as input or by creating a custom delegate
#MethodMan:
I tried to implement the solution in that question, but I am getting the same error. From what I understood, changing the Expression.Parameter from typeof(object) to typeof(object[]) should have worked in my case, but I get the same error
EDIT:
Here's the modified version of what I tried from the linked version:
class Program
{
static void Main(string[] argas)
{
Type[] types = new Type[] { typeof(object[]) };
var constructorInfo = typeof(Program).GetConstructor(types);
var parameters = types.Select((t, i) => Expression.Parameter(t, "p" + i)).ToArray();
var someType1Exp = Expression.New(constructorInfo, parameters);
var inner = Expression.Lambda(someType1Exp, parameters);
var args = Expression.Parameter(typeof(object[]), "args");
var body = Expression.Invoke(inner,
parameters.Select((p, i) => Expression.Convert(Expression.ArrayIndex(args, Expression.Constant(i)), p.Type)).ToArray());
var outer = Expression.Lambda<Func<object[], object>>(body, args);
var func = outer.Compile();
object[] values = { 1, 123.45F, "abc" };
object obj = func(values);
Console.WriteLine(obj);
Console.WriteLine("test");
Console.ReadLine();
}
//public Program() { }
public Program(object[] values) { Console.WriteLine(values.ToString()); }
}
I get the following error:
System.InvalidCastException: 'Unable to cast object of type 'System.Int32' to type 'System.Object[]'.'
You're problem is you changed your lambda to take a single array argument, object[] but you are still passing in multiple arguments (paramValues). You need to pass in a single value, the Array. You will also need to modify your expression body to expect an array.
Consider before you had the equivalent of
(a,b,c,d) => new Program(a, b, c, d)
Now you want
(object[] a) => new Program((type0)a[0], (type1)a[1], (type2)a[2], (type3)a[3])
So you need to modify your code to reference the elements of the parameter array instead of the parameters directly:
class Program
{
static void Main(string[] argas)
{
var constructorInfo = typeof(Program).GetConstructors()[0];
var types = constructorInfo.GetParameters().Select(p => p.ParameterType);
var parm = Expression.Parameter(typeof(object[]), "args");
var parameters = types.Select((t, i) => Expression.Convert(Expression.ArrayIndex(parm, Expression.Constant(i)), t)).ToArray();
var someType1Exp = Expression.New(constructorInfo, parameters);
var outer = Expression.Lambda<Func<object[], object>>(someType1Exp, parm);
var func = outer.Compile();
object[] values = { 1, 123.45F, "abc" };
object obj = func(values);
Console.WriteLine(obj);
Console.WriteLine("test");
Console.ReadLine();
}
//public Program() { }
public Program(int val1, float val2, string val3) {
Console.WriteLine(val1);
Console.WriteLine(val2);
Console.WriteLine(val3);
}
}
If you want to pass the object[] to the constructor, as in
(object[] args) => new Program(args)
you can just do that directly (sorry for formatting, copied from my LINQPad):
public class Program {
void Main() {
var constructorInfo = typeof(Program).GetConstructors()[0];
var parm = Expression.Parameter(typeof(object[]), "args");
var someType1Exp = Expression.New(constructorInfo, parm);
var outer = Expression.Lambda<Func<object[], object>>(someType1Exp, parm);
var func = outer.Compile();
object[] values = { 1, 123.45F, "abc" };
object obj = func(values);
Console.WriteLine(obj);
Console.WriteLine("test");
}
// Define other methods and classes here
public Program(object[] parms) {
Console.WriteLine(parms.ToString());
}
}

dynamically create delegate for ctor

I'm trying to create generic factory class. Since Activator.CreateInstance is pretty slow, I decided to use delegates. The goal was call constructor any public constructor, regardless of parameters count. So I was going like this:
public void Register<VType>(TKey key, params object[] args) where VType : TType
{
ConstructorInfo ci = typeof(VType).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.HasThis, args.Select(a => a.GetType()).ToArray(), new ParameterModifier[] { });
if (ci == null)
throw new InvalidOperationException(string.Format("Constructor for type '{0}' was not found.", typeof(VType)));
var pExp = Expression.Parameter(args.GetType());
var ctorParams = ci.GetParameters();
var expArr = new Expression[ctorParams.Length];
var p = new ParameterExpression[ctorParams.Length];
for (var i = 0; i < ctorParams.Length; i++)
{
var ctorType = ctorParams[i].ParameterType;
var pName = ctorParams[i].Name;
var argExp = Expression.ArrayIndex(pExp, Expression.Constant(i));
var argExpConverted = Expression.Convert(argExp, ctorType);
expArr[i] = argExpConverted;
p[i] = Expression.Parameter(args[i].GetType(), pName);
}
var foo = Expression.Lambda(Expression.New(ci, expArr), p);
Delegate constructorDelegate = foo.Compile();
FactoryMap.Add(key, constructorDelegate);
}
And then - call delegate in Create method. With no parameters all goes well, but when I'm adding some - I'm getting InvalidOperationException - "variable '' of type 'System.Object[]' referenced from scope '', but it is not defined", after foo.Compile() call.
Why? How can I resolve this issue?
Below is a class that exposes an extention method that gives you a delegate for creating an instance of type T by calling the constructor that binds to specified paramArguments types.
public static class ConstructorCallExcentions
{
private static Dictionary<ConstructorInfo, Func<Object[], Object>> _constructors = new Dictionary<ConstructorInfo,Func<object[],object>> ();
private static object syncObject = new object();
public static Func<Object[], Object> CreateConstructor<T>(this T #this, params Type[] paramArguments)
{
ConstructorInfo cInfo = typeof(T).GetConstructor(paramArguments);
if (cInfo == null)
throw new NotSupportedException("Could not detect constructor having the coresponding parameter types");
Func<Object[], Object> ctor;
if (false == _constructors.TryGetValue (cInfo, out ctor))
{
lock (_constructors)
{
if (false == _constructors.TryGetValue (cInfo, out ctor))
{
// compile the call
var parameterExpression = Expression.Parameter(typeof(object[]), "arguments");
List<Expression> argumentsExpressions = new List<Expression>();
for (var i = 0; i < paramArguments.Length; i++)
{
var indexedAcccess = Expression.ArrayIndex(parameterExpression, Expression.Constant(i));
// it is NOT a reference type!
if (paramArguments [i].IsClass == false && paramArguments [i].IsInterface == false)
{
// it might be the case when I receive null and must convert to a structure. In this case I must put default (ThatStructure).
var localVariable = Expression.Variable(paramArguments[i], "localVariable");
var block = Expression.Block (new [] {localVariable},
Expression.IfThenElse (Expression.Equal (indexedAcccess, Expression.Constant (null)),
Expression.Assign (localVariable, Expression.Default (paramArguments [i])),
Expression.Assign (localVariable, Expression.Convert(indexedAcccess, paramArguments[i]))
),
localVariable
);
argumentsExpressions.Add(block);
}
else
argumentsExpressions.Add(Expression.Convert(indexedAcccess, paramArguments[i])); // do a convert to that reference type. If null, the convert is FINE.
}
// check if parameters length maches the length of constructor parameters!
var lengthProperty = typeof (Object[]).GetProperty ("Length");
var len = Expression.Property (parameterExpression, lengthProperty);
var invalidParameterExpression = typeof(ArgumentException).GetConstructor(new Type[] { typeof(string) });
var checkLengthExpression = Expression.IfThen (Expression.NotEqual (len, Expression.Constant (paramArguments.Length)),
Expression.Throw(Expression.New(invalidParameterExpression, Expression.Constant ("The length does not match parameters number")))
);
var newExpr = Expression.New(cInfo, argumentsExpressions);
var finalBlock = Expression.Block(checkLengthExpression, Expression.Convert(newExpr, typeof(Object)));
_constructors[cInfo] = ctor = Expression.Lambda(finalBlock, new[] { parameterExpression }).Compile() as Func<Object[], Object>;
}
}
}
return ctor;
}
}
To use it, for example supose you have this class:
public class Test
{
public Test(string s, int h)
{
Console.Write("aaa");
}
}
Then write this code:
var ctor = default(Test).CreateConstructor(typeof(string), typeof(int));
var newlyObject = ctor(new object[] { "john", 22 });
From your example, I saw that your intentions are to use the Delegate to invoke later any constructor. Instead of using Delegate and the DynamicInvoke API, use my
Func <Object[], Object>.
Why? Here is a couple of advantages that I have in mind right now:
1) DynamicInvoke is much slower than calling a direct typed delegate.
2) DynamicInvoke will break any stack trace in case of an exception. What I mean is that whenever an exception is thrown in the constructor, you will receive a TargetInvocationException instead of the real exception that happened. You can inspect the InnerException of that TargetInvocationException but ... clear is more work to do. Calling directly the typed delegate Func will save you from this issue.
Happy coding!

Categories