2015-12-18 19 views
5

我想創建一個方法(在運行時)爲所有類型的委託創建包裝。這爲創建注入附加日誌記錄的靈活方式(在這種情況下)。在這第一步中,我試圖圍繞給定的input-參數創建一個try-catch環繞。「變量」的類型'System.Boolean'從範圍''引用,但它沒有被定義「在表達式

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

對於無效的方法(如Action<>)這個代碼做什麼,我需要的。但是,當有一個返回值,我得到的例外「‘式的'變量’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: 
    } 
} 

我在想什麼? (在調試小時我嘗試:

  • 從試體內部移動Expression.Variable到頂層
  • 經由typed-得到追趕塊相同Body.Type作爲在try塊。 Expression.Return

回答

0

看起來你沒有指定的語句塊你varaibles。

在你創建的飛行參數,而不是給它起名字,如果你做了,你就會看到錯誤:

"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(); 
相關問題