a "variable" of type "System.Boolean" that refers to the scope "'but not defined" in the expression

I am trying to create a method for (at runtime) creating wrappers for delegates of all types. This will create a flexible way to enter additional logs (in this case). In this first step, I tried to create a try-catch wrapper around a given input -argument.

 try { Console.WriteLine(....); // Here the original call Console.WriteLine(....); } catch(Exception ex) { Console.WriteLine(.....); } 

I use a generic call to the CreateWrapper2 method (see below)

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

For void methods (e.g. Action<> ) this code does what I need. But when there is a return value, I get a "variable" exception of type "System.Boolean" that refers to the scope of "but not defined"

Many other posts talk about Expression.Parameter called more than once for a parameter; it seems to me that something is wrong here, but I cannot find it. Everything goes well to the .Compile line, there it crashes.

For Func<int, bool> target = i => i % 2 ==0; Below is the DebugView for the generated expression.

 .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: } } 

What am I missing? (during debug hours, I tried:

  • move Expression.Variable from inside the body try to thevelvel.
  • gave the catch block the same Body.Type as the try-block, using typed- Expression.Return .

)

+5
source share
1 answer

It looks like you did not specify your variables for the block statement.

In an error, you create a parameter "on the fly" and do not give it a name if you had seen it:

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

So, for future reference, you can make your life much easier if you give the names of your vars when creating expression trees, the following should work

  // 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(); 
0
source

Source: https://habr.com/ru/post/1238636/


All Articles