"variable '' of type 'System.Boolean' referenced from scope '', but it is not defined" 在表达式中

"variable '' of type 'System.Boolean' referenced from scope '', but it is not defined" in Expression

我正在尝试创建一种方法(在运行时)为所有类型的委托创建包装器。这是为了创建一种灵活的方式来注入额外的日志记录(在本例中)。在第一步中,我尝试围绕给定的 input-argument.

创建一个 try-catch 环绕
try
{
  Console.WriteLine(....);
  // Here the original call
  Console.WriteLine(....);
}
catch(Exception ex)
{
  Console.WriteLine(.....);
}

我正在使用通用方法调用 CreateWrapper2(见下文)

private static readonly MethodInfo ConsoleWriteLine = typeof(Console).GetMethod("WriteLine", new[] { typeof(string), typeof(object[]) });

private static MethodCallExpression WriteLinExpression(string format, params object[] args)
{
    Expression[] expressionArguments = new Expression[2];
    expressionArguments[0] = Expression.Constant(format, typeof(string));
    expressionArguments[1] = Expression.Constant(args, typeof(object[]));

    return Expression.Call(ConsoleWriteLine, expressionArguments);
}

public T CreateWrapper2<T>(T input)
{
    Type type = typeof(T);

    if (!typeof(Delegate).IsAssignableFrom(type))
    {
        return input;
    }

    PropertyInfo methodProperty = type.GetProperty("Method");
    MethodInfo inputMethod = methodProperty != null ? (MethodInfo)methodProperty.GetValue(input) : null;

    if (inputMethod == null)
    {
        return input;
    }

    string methodName = inputMethod.Name;
    ParameterInfo[] parameters = inputMethod.GetParameters();
    ParameterExpression[] parameterExpressions = new ParameterExpression[parameters.Length];

    // TODO: Validate/test parameters, by-ref /out with attributes etc.

    for (int idx = 0; idx < parameters.Length; idx++)
    {
        ParameterInfo parameter = parameters[idx];
        parameterExpressions[idx] = Expression.Parameter(parameter.ParameterType, parameter.Name);
    }

    bool handleReturnValue = inputMethod.ReturnType != typeof(void);

    ParameterExpression variableExpression = handleReturnValue ? Expression.Variable(inputMethod.ReturnType) : null;
    MethodCallExpression start = WriteLinExpression("Starting '{0}'.", methodName);
    MethodCallExpression completed = WriteLinExpression("Completed '{0}'.", methodName);
    MethodCallExpression failed = WriteLinExpression("Failed '{0}'.", methodName);

    Expression innerCall = Expression.Call(inputMethod, parameterExpressions);
    LabelTarget returnTarget = Expression.Label(inputMethod.ReturnType);
    LabelExpression returnLabel = Expression.Label(returnTarget, Expression.Default(returnTarget.Type)); ;
    GotoExpression returnExpression = null;

    if (inputMethod.ReturnType != typeof(void))
    {
        // Handle return value.
        innerCall = Expression.Assign(variableExpression, innerCall);
        returnExpression = Expression.Return(returnTarget, variableExpression, returnTarget.Type);
    }
    else
    {
        returnExpression = Expression.Return(returnTarget);
    }

    List<Expression> tryBodyElements = new List<Expression>();
    tryBodyElements.Add(start);
    tryBodyElements.Add(innerCall);
    tryBodyElements.Add(completed);

    if (returnExpression != null)
    {
        tryBodyElements.Add(returnExpression);
    }

    BlockExpression tryBody = Expression.Block(tryBodyElements);
    BlockExpression catchBody = Expression.Block(tryBody.Type, new Expression[] { failed, Expression.Rethrow(tryBody.Type) });
    CatchBlock catchBlock = Expression.Catch(typeof(Exception), catchBody);
    TryExpression tryBlock = Expression.TryCatch(tryBody, catchBlock);

    List<Expression> methodBodyElements = new List<Expression>();

    if(variableExpression != null) methodBodyElements.Add(variableExpression);

    methodBodyElements.Add(tryBlock);
    methodBodyElements.Add(returnLabel);

    Expression<T> wrapperLambda = Expression<T>.Lambda<T>(Expression.Block(methodBodyElements), parameterExpressions);

    Console.WriteLine("lambda:");
    Console.WriteLine(wrapperLambda.GetDebugView());

    return wrapperLambda.Compile();
}

对于 void 方法(如 Action<>),这段代码可以满足我的需要。但是当有一个 return 值时,我得到异常“ 类型 'System.Boolean' 的变量 '' 从范围 '' 引用,但它没有定义

许多其他帖子都在讨论 Expression.Parameter 为一个参数调用了不止一次;对我来说,这里似乎有其他问题,但我找不到。一切顺利,直到 .Compile 行崩溃。

下面的 Func<int, bool> target = i => i % 2 ==0; 是生成表达式的 DebugView。

.Lambda #Lambda1<System.Func`2[System.Int32,System.Boolean]>(System.Int32 $i) {
    .Block() {
        $var1;
        .Try {
            .Block() {
                .Call System.Console.WriteLine(
                    "Starting '{0}'.",
                    .Constant<System.Object[]>(System.Object[]));
                $var1 = .Call LDAP.LdapProgram.<Main>b__0($i);
                .Call System.Console.WriteLine(
                    "Completed '{0}'.",
                    .Constant<System.Object[]>(System.Object[]));
                .Return #Label1 { $var1 }
            }
        } .Catch (System.Exception) {
            .Block() {
                .Call System.Console.WriteLine(
                    "Failed '{0}'.",
                    .Constant<System.Object[]>(System.Object[]));
                .Rethrow
            }
        };
        .Label
            .Default(System.Boolean)
        .LabelTarget #Label1:
    }
}

我错过了什么? (在调试期间我试过:

)

您似乎没有为块语句指定变量。

在错误中,您正在动态创建一个参数并且没有给它命名,如果您这样做了,您会看到:

"variable 'varName' of type 'System.Boolean' referenced from scope 'varName', but it is not defined"

因此,为了将来参考,如果您在制作表达式树时给您的变量命名,那么以下内容应该有效

        // Define the variable at the top of the block 
        // when we are returning something
        if (variableExpression != null)
        {
            block = Expression.Block(new[] { variableExpression }, methodBodyElements);
        }
        else
        {
            block = Expression.Block(methodBodyElements);
        }

        Expression<T> wrapperLambda = Expression<T>.Lambda<T>(block, parameterExpressions);

        return wrapperLambda.Compile();