Levon Ravel
Levon Ravel

Reputation: 100

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

Upvotes: 2

Views: 621

Answers (1)

Nkosi
Nkosi

Reputation: 246998

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

Upvotes: 4

Related Questions