I have this code snippet that set Images in list view control.
delegate void SetImageListDataCallback(string imgName, int i);
void SetImageListData(string imgName, int i)
{
if (lvFoundModelImages.InvokeRequired)
{
SetImageListDataCallback c = new SetImageListDataCallback(imgName, i);
this.Invoke(c, new object[]
{
imgName,i
});
}
else
{
lvFoundModelImages.LargeImageList = imageList1;
lvFoundModelImages.Items.Add(new ListViewItem(imgName, i));
}
}
But the problem is I am getting errors in this line:
SetImageListDataCallback c = new SetImageListDataCallback(imgName, i);
in the first parameter,
ImgName is not null here
. while the second parameter "i" showing
method name expected
You are using a wrong syntax to create a new instance of the SetImageListDataCallback. You should pass amethod with a matching signature as the first parameter of the delegate constructor:
SetImageListDataCallback c = new SetImageListDataCallback(SetImageListData);
Then, you can pass the imgName and i parameters as arguments at the invocation time:
this.Invoke(c, new object[] { imgName, i });
Related
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.
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.
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());
}
}
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!
After some resent tests I have found my implementation cannot handle very much recursion. Although after I ran a few tests in Firefox I found that this may be more common than I originally thought. I believe the basic problem is that my implementation requires 3 calls to make a function call. The first call is made to a method named Call that makes sure the call is being made to a callable object and gets the value of any arguments that are references. The second call is made to a method named Call which is defined in the ICallable interface. This method creates the new execution context and builds the lambda expression if it has not been created. The final call is made to the lambda that the function object encapsulates. Clearly making a function call is quite heavy but I am sure that with a little bit of tweaking I can make recursion a viable tool when using this implementation.
public static object Call(ExecutionContext context, object value, object[] args)
{
var func = Reference.GetValue(value) as ICallable;
if (func == null)
{
throw new TypeException();
}
if (args != null && args.Length > 0)
{
for (int i = 0; i < args.Length; i++)
{
args[i] = Reference.GetValue(args[i]);
}
}
var reference = value as Reference;
if (reference != null)
{
if (reference.IsProperty)
{
return func.Call(reference.Value, args);
}
else
{
return func.Call(((EnviromentRecord)reference.Value).ImplicitThisValue(), args);
}
}
return func.Call(Undefined.Value, args);
}
public object Call(object thisObject, object[] arguments)
{
var lexicalEnviroment = Scope.NewDeclarativeEnviroment();
var variableEnviroment = Scope.NewDeclarativeEnviroment();
var thisBinding = thisObject ?? Engine.GlobalEnviroment.GlobalObject;
var newContext = new ExecutionContext(Engine, lexicalEnviroment, variableEnviroment, thisBinding);
Engine.EnterContext(newContext);
var result = Function.Value(newContext, arguments);
Engine.LeaveContext();
return result;
}
I cannot believe how easy this was to get working. Basically in my compiler I check to see if the function is returning the result of calling itself. If so I instead return the arguments that are being passed. Then I simply grab any reference values and re-invoke the backing lambda. With this in place I was able to make millions of recursive calls.
I would like to thank DrJokepu for inspiring this solution.
public object Call(object thisObject, object[] arguments)
{
var lexicalEnviroment = Scope.NewDeclarativeEnviroment();
var variableEnviroment = Scope.NewDeclarativeEnviroment();
var thisBinding = thisObject ?? Engine.GlobalEnviroment.GlobalObject;
var newContext = new ExecutionContext(Engine, lexicalEnviroment, variableEnviroment, thisBinding);
var result = default(object);
var callArgs = default(object[]);
Engine.EnterContext(newContext);
while (true)
{
result = Function.Value(newContext, arguments);
callArgs = result as object[];
if (callArgs == null)
{
break;
}
for (int i = 0; i < callArgs.Length; i++)
{
callArgs[i] = Reference.GetValue(callArgs[i]);
}
arguments = callArgs;
}
Engine.LeaveContext();
return result;
}