2010-09-05 2 views
7

Я пытаюсь построить динамическое дерево выражений, как показано ниже:Проблема при построении динамического Expression Tree

Func<IEnumerable<int>, int, bool> dividesectionmethod = (x, y) => 
      { 
       int nos1 = 0; 
       int nos2 = 0; 
       foreach (int i in x) 
       { 
        if (i <= y) 
         nos1++; 
        else 
         nos2++; 
       } 
       return nos1 > nos2; 
      }; 

Для этого я использую:

ParameterExpression enumerableExpression = Expression.Parameter(typeof(IEnumerable<int>), "x"); 
      ParameterExpression intexpression = Expression.Parameter(typeof(int), "y"); 

      ParameterExpression localvarnos1 = Expression.Variable(typeof(int), "nos1"); 
      ParameterExpression localvarnos2 = Expression.Variable(typeof(int), "nos2"); 
      ConstantExpression zeroConstantintval = Expression.Constant(0); 
      BinaryExpression bexplocalnos1 = Expression.Assign(localvarnos1, zeroConstantintval); 
      BinaryExpression bexplocalnos2 = Expression.Assign(localvarnos2, zeroConstantintval); 

      //As Expression does not support Foreach we need to get Enumerator before doing loop 

      ParameterExpression enumerator = Expression.Variable(typeof(IEnumerator<int>), "enumerator"); 
      BinaryExpression assignenumerator = Expression.Assign(enumerator, Expression.Call(enumerableExpression, typeof(IEnumerable<int>).GetMethod("GetEnumerator"))); 


      var currentelement = Expression.Parameter(typeof(int), "i"); 
      var callCurrent = Expression.Assign(currentelement, Expression.Property(enumerator, "Current")); 

      BinaryExpression firstlessequalsecond = Expression.LessThanOrEqual(currentelement, intexpression); 

      MethodCallExpression movenext = Expression.Call(enumerator, typeof(IEnumerator).GetMethod("MoveNext")); 

      LabelTarget looplabel = Expression.Label("looplabel"); 
      LabelTarget returnLabel = Expression.Label(typeof(bool), "retval"); 

      BlockExpression block = Expression.Block(enumerableExpression, intexpression, localvarnos1, localvarnos2, 
       bexplocalnos1, bexplocalnos2, Expression.Loop(Expression.IfThenElse(
       Expression.NotEqual(movenext, Expression.Constant(false)), 
       Expression.IfThenElse(firstlessequalsecond, Expression.Increment(localvarnos1), Expression.Increment(localvarnos2)),Expression.Break(looplabel)), looplabel), 
       Expression.Return(returnLabel, Expression.LessThan(localvarnos1, localvarnos2))); 

      Expression<Func<IEnumerable<int>, int, bool>> lambda = Expression.Lambda<Func<IEnumerable<int>, int, bool>>(block, Expression.Parameter(typeof(IEnumerable<int>), "x"), 
       Expression.Parameter(typeof(int), "y")); 


      Func<IEnumerable<int>, int, bool> mymethod = lambda.Compile(); 

Но проблема Expression.Lambda бросает исключение:

Expression of type 'System.Void' cannot be used for return type 'System.Boolean' 

Я не знаю, вопрос, как мой блок, кажется Ok:

.Block() { 
    $x; 
    $y; 
    $nos1; 
    $nos2; 
    $nos1 = 0; 
    $nos2 = 0; 
    .Loop { 
     .If (.Call $enumerator.MoveNext() != False) { 
      .If ($i <= $y) { 
       .Increment($nos1) 
      } .Else { 
       .Increment($nos2) 
      } 
     } .Else { 
      .Break looplabel { } 
     } 
    } 
    .LabelTarget looplabel:; 
    .Return retval { $nos1 < $nos2 } 
} 

Пожалуйста, дайте мне знать, в чем проблема.

ответ

10

Значение BlockExpression - это просто значение последнего выражения в блоке. Вместо того, чтобы включать ReturnExpression, просто пусть последнее выражение будет значением, которое вы хотите вернуть.

Кроме того, вам необходимо объявить ParameterExpressions для блока как отдельный аргумент метода Expression.Block. Вы включили их в список выражений, которые заставят их оцениваться как выражения, но не будут объявлять их в блоке.

Кроме того, Expression.Increment «не изменяет значение передаваемого ему объекта», поэтому вам нужно будет обернуть выражения приращения в выражениях присваивания.

BlockExpression block = Expression.Block(
    new ParameterExpression[] { 
     localvarnos1, localvarnos2, enumerator, currentelement }, 
    bexplocalnos1, 
    bexplocalnos2, 
    assignenumerator, 
    Expression.Loop(
     Expression.IfThenElse(
      Expression.NotEqual(movenext, Expression.Constant(false)), 
      Expression.Block(
       callCurrent, 
       Expression.IfThenElse(
        firstlessequalsecond, 
        Expression.Assign(
         localvarnos1, 
         Expression.Increment(localvarnos1)), 
        Expression.Assign(
         localvarnos2, 
         Expression.Increment(localvarnos2)))), 
      Expression.Break(looplabel)), 
     looplabel), 
    Expression.LessThan(localvarnos1, localvarnos2)); 

Expression<Func<IEnumerable<int>, int, bool>> lambda = 
    Expression.Lambda<Func<IEnumerable<int>, int, bool>>(
     block, 
     enumerableExpression, 
     intexpression); 
+0

Ох ... спасибо вам за помощь. На самом деле я забыл, что мне нужно назначить для Приращения. – abhishek