【问题标题】:"variable '' of type 'System.Boolean' referenced from scope '', but it is not defined" in Expression表达式中的“从范围''引用的'System.Boolean'类型的变量'',但未定义”
【发布时间】:2015-12-18 13:18:18
【问题描述】:

我正在尝试创建一种方法,用于(在运行时)为所有类型的委托创建包装器。这创建了一种灵活的方式来注入额外的日志记录(在这种情况下)。在这第一步中,我尝试围绕给定的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&lt;&gt;),此代码可以满足我的需要。但是当有一个返回值时,我得到了异常“从范围''引用的'System.Boolean'类型的变量'',但它没有定义

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

对于Func&lt;int, bool&gt; target = i =&gt; 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:
    }
}

我错过了什么? (在我尝试的调试时间:

  • Expression.Variable 从 try-body 内部移动到顶层。
  • 通过 typed-Expression.Return 为 catch-block 赋予与 try-block 相同的 Body.Type。

)

【问题讨论】:

    标签: c# expression


    【解决方案1】:

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

    在错误中,您正在动态创建参数而不给它命名,如果您这样做了,您会看到:

    "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();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-09
      • 1970-01-01
      • 2020-01-10
      • 2015-10-08
      相关资源
      最近更新 更多