2012-01-31 2 views
2
그것은 문자열과 객체의 쌍 (플레이어 및 세계)

Func을 <문자열, 부울> 식 트리에 사용하기 위해 bool로하는

각 노드에 소요 내가 지나치게 복잡한 이진 표현 트리 빌딩 시스템을 가지고

트리는 문자열, 플레이어 및 세계를 취하는 외부 함수를 나타내며 bool (테스트 용) 문자열 (출력용) 또는 void (작업 용)를 반환합니다.

내 문제는 세 가지입니다. 첫째로 나는 Expression.Condition 또는 Expression.IfThenElse과 같은 것을 사용하려면 시험 표현식이 Expresson<bool>이 아닌 Expression<func<string, Player, World, bool>> (Expression.And가 출력 됨)

두 번째로 Player와 World의 메모리 참조가 동일하게 유지되어야합니다. 트리의 노드 중 하나가 Player 내에서 무언가를 업데이트하는 경우 다음과 같이 업데이트됩니다. 다음 노드.

마지막으로 나는 모든 문자열을 하나씩 추가해야합니다. 열심히 나무를 코딩 할 수 있다면

,이 같은 뭔가를 찾고 끝낼 수 있습니다 :

class Main 
    { 
     string Foo(string text, World world, Player player) 
     { 
      string output; 
      output += SomeClass.PrintStarting(); 
      if (SomeClass.Exists(text, world, player)) 
      { 
       output += SomeClass.PrintName(text, world, player); 
       SomeClass.KillPlayer(text, world, player); 
       if (SomeClass.Exists(text, world, player)) 
        output += SomeClass.PrintSurvived(text, world, player); 
      } 
      else 
       output += SomeClass.PrintNotExists(text, world, player); 
      return output; 
     } 
    } 
    public class SomeClass 
    { 
     string PrintStart(string text, World world, Player player) 
     { 
      return "Starting.\n"; 
     } 

     bool Exists(string text, World world, Player player) 
     { 
      player.Lives; 
     } 

     string PrintName(string text, World world, Player player) 
     { 
      return player.Name + ".\n"; 
     } 

     string PrintSurvived(string text, World world, Player player) 
     { 
      return player.Name + "died.\n"; 
     } 

     string PrintNotExists(string text, World world, Player player) 
     { 
      return "This person does not exist.\n"; 
     } 

     void KillPlayer(string text, World world, Player player) 
     { 
      if (text != "kidding") 
       player.Lives = false; 
     } 
    } 

더 정교하게하려면 나는 그것의 시험/지정/문자열의 모든 메소드와 SomeClass의 인스턴스를 가지고있다. 그런 다음 Expression<func<string[], World, Player, bool>>, Expression<Action<string[], World, Player>>Expression<func<string[], World, Player, string>>의 목록을 만들고이를 함께 트리에 넣기 시작합니다. 내가 (예를 들어) 나를 떠나 처리 한 어떤 일이 일어나는지의 실제 순서 :

public string Foo2(string text, World world, Player player) 
    { 
     ParameterExpression result = Expression.Parameter(typeof(string), "result"); 
     ParameterExpression inputString = Expression.Parameter(typeof(string[]), "inputString"); 
     ParameterExpression inputWorld = Expression.Parameter(typeof(World), "inputWorld"); 
     ParameterExpression inputPlayer = Expression.Parameter(typeof(Player), "inputPlayer"); 
     System.Reflection.MethodInfo methodInfo = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string) }); 

     Expression textPrintStarting = (Expression<Func<string, World, Player, string>>)((Text, World, Player) => SomeClass.PrintStarting(Text, World, Player)); 
     Expression testExists = (Expression<Func<string, World, Player, bool>>)((Text, World, Player) => SomeClass.Exists(Text, World, Player)); 
     Expression textPrintName = (Expression<Func<string, World, Player, string>>)((Text, World, Player) => SomeClass.PrintName(Text, World, Player)); 
     Expression killPlayer = (Expression<Action<string, World, Player>>)((Text, World, Player) => SomeClass.KillPlayer(Text, World, Player)); 
     Expression textPrintSurvived = (Expression<Func<string, World, Player, string>>)((Text, World, Player) => SomeClass.PrintSurvived(Text, World, Player)); 
     Expression textPrintNotExist = (Expression<Func<string, World, Player, string>>)((Text, World, Player) => SomeClass.PrintNotExists(Text, World, Player)); 


     Expression innerTest = 
      Expression.Condition(
       Expression.Invoke(Expression.Lambda<Func<string, World, Player, bool>>(testExists, inputString, inputWorld, inputPlayer)), 
       Expression.Assign(result, Expression.Call(methodInfo, result, Expression.Lambda<Func<string, World, Player, string>>(textPrintSurvived, inputString, inputWorld, inputPlayer))), 
       Expression.Empty()); 

     Expression success = 
      Expression.Block(
       Expression.Assign(result, Expression.Call(methodInfo, result, Expression.Lambda<Func<string, World, Player, string>>(textPrintName, inputString, inputWorld, inputPlayer))), 
       Expression.Lambda<Action<string, World, Player>>(killPlayer, inputString, inputWorld, inputPlayer), 
       innerTest); 

     Expression failure = 
      Expression.Assign(result, Expression.Call(methodInfo, result, Expression.Lambda<Func<string, World, Player, string>>(textPrintNotExist, inputString, inputWorld, inputPlayer))); 

     Expression outerTest = 
      Expression.Condition(
       Expression.Invoke(Expression.Lambda<Func<string, World, Player, bool>>(testExists, inputString, inputWorld, inputPlayer)), 
       success, 
       failure); 

     Expression finalExpression = 
      Expression.Block(
       Expression.Assign(result, Expression.Call(methodInfo, result, Expression.Lambda<Func<string, World, Player, string>>(textPrintStarting, inputString, inputWorld, inputPlayer))), 
       outerTest); 

     return Expression.Lambda<Func<string, World, Player, string>>(
       Expression.Block(new[] { result }, 
       finalExpression)).Compile()(text, world, player); 
    } 

문제는이 bool로하는 Func을 변환 할 수 없기 때문에 오류를 던져 Condition 문에 있습니다. 매개 변수가 전달되는지 여부도 확실하지 않습니다. (디버그 할 수 없었기 때문에)

+0

당신은 자세히 설명해 주시겠습니까? 당신의 문제에서 당신이 말하는 말이 나에게 의미가 없습니다. 당신은 무엇을 가지고 있습니까? (구체적인 예를 들어주십시오)? 그리고 무엇을하고 싶습니까 (다시, 구체적으로 기재하십시오)?어떤 표현이 있고 그것을 어떻게 사용 하시겠습니까? –

+0

문제점의 현재 구현에 대한 추가 세부 사항을 추가했습니다. – bigjokerfish

+0

모든 것을 표현식으로 사용하려는 특별한 이유가 있습니까? 나는 그것을 전혀 필요가 없다. 표현식의 예제가 필요하고 실제로 가능한지 확인하려면 함수를 람다 식으로 저장할 수 있습니다. –

답변

2

많은 MethodInfo와 물놀이 후, 나는 기록 할 때 발견 :

Expression innerTest = 
    Expression.Condition(
     Expression.Invoke(Expression.Lambda<Func<string, World, Player, bool>>(testExists, inputString, inputWorld, inputPlayer)), 
     Expression.Assign(result, Expression.Call(methodInfo, result, Expression.Lambda<Func<string, World, Player, string>>(textPrintSurvived, inputString, inputWorld, inputPlayer))), 
     Expression.Empty()); 

Expression.Lambda 선회하여 코드에 복잡함을 더하고있었습니다. Func<string, World, Player, string>Func<string, World, Player, Func<string, World, Player, string>>

Expression.Invoke은 처음에 나를 혼란스럽게 만들었습니다. 이 놀라운 계시로 나는 이것을 다음과 같이 업데이트했다 :

Expression innerTest = 
     Expression.IfThen(
      Expression.Invoke(testExists, inputString, inputWorld, inputPlayer), 
      Expression.Assign(result, Expression.Call(methodInfo, result, Expression.Lambda<Func<string, World, Player, string>>(textPrintSurvived, inputString, inputWorld, inputPlayer)))); 
0

해당 코드를 표현식으로 표현한 것입니다. 이것이 내가 생각해내는 것입니다. 그것이 모든 경우에 의도 된대로 작동하는지 모르겠지만 컴파일되고 내 테스트에서 작동하는 것 같습니다. 여기

// helper method 
static Expression AddAssignStrings(ParameterExpression left, Expression right) 
{ 
    var stringType = typeof(string); 
    var concatMethod = stringType.GetMethod("Concat", new[] { stringType, stringType }); 
    return Expression.Assign(
     left, 
     Expression.Call(concatMethod, left, right) 
    ); 
} 

var text = Expression.Parameter(typeof(string), "text"); 
var world = Expression.Parameter(typeof(World), "world"); 
var player = Expression.Parameter(typeof(Player), "player"); 
var output = Expression.Variable(typeof(string), "output"); 

// looks safe to reuse this array for the expressions 
var arguments = new ParameterExpression[] { text, world, player }; 

var someClassType = typeof(SomeClass); 
// assuming the methods are all publicly accessible 
var printStartingMethod = someClassType.GetMethod("PrintStarting"); 
var existsMethod = someClassType.GetMethod("Exists"); 
var printNameMethod = someClassType.GetMethod("PrintName"); 
var killPlayerMethod = someClassType.GetMethod("KillPlayer"); 
var printSurvivedMethod = someClassType.GetMethod("PrintSurvived"); 
var printNotExistsMethod = someClassType.GetMethod("PrintNotExists"); 

var ifTrueBlockContents = new Expression[] 
{ 
    AddAssignStrings(output, Expression.Call(printNameMethod, arguments)), 

    Expression.Call(killPlayerMethod, arguments), 

    Expression.IfThen(
     Expression.Call(existsMethod, arguments), 
     AddAssignStrings(output, Expression.Call(printSurvivedMethod, arguments)) 
    ), 
}; 

var blockContents = new Expression[] 
{ 
    Expression.Assign(output, Expression.Call(printStartingMethod)), 

    Expression.IfThenElse(
     Expression.Call(existsMethod, arguments), 
     Expression.Block(ifTrueBlockContents), 
     AddAssignStrings(output, Expression.Call(printNotExistsMethod, arguments)) 
    ), 

    output, 
}; 

var body = Expression.Block(typeof(string), new ParameterExpression[] { output }, blockContents); 

var lambda = Expression.Lambda<Func<string, World, Player, string>>(body, arguments); 

// reference method 
static string Foo(string text, World world, Player player) 
{ 
    string output = SomeClass.PrintStarting(); 
    if (SomeClass.Exists(text, world, player)) 
    { 
     output += SomeClass.PrintName(text, world, player); 
     SomeClass.KillPlayer(text, world, player); 
     if (SomeClass.Exists(text, world, player)) 
      output += SomeClass.PrintSurvived(text, world, player); 
    } 
    else 
     output += SomeClass.PrintNotExists(text, world, player); 
    return output; 
} 
는 식의 디버그 뷰입니다 :

.Lambda #Lambda1<System.Func`4[System.String,Test.World,Test.Player,System.String]>(
    System.String $text, 
    Test.World $world, 
    Test.Player $player) { 
    .Block(System.String $output) { 
     $output = .Call Test.SomeClass.PrintStarting(); 
     .If (
      .Call Test.SomeClass.Exists(
       $text, 
       $world, 
       $player) 
     ) { 
      .Block() { 
       $output = .Call System.String.Concat(
        $output, 
        .Call Test.SomeClass.PrintName(
         $text, 
         $world, 
         $player)); 
       .Call Test.SomeClass.KillPlayer(
        $text, 
        $world, 
        $player); 
       .If (
        .Call Test.SomeClass.Exists(
         $text, 
         $world, 
         $player) 
       ) { 
        $output = .Call System.String.Concat(
         $output, 
         .Call Test.SomeClass.PrintSurvived(
          $text, 
          $world, 
          $player)) 
       } .Else { 
        .Default(System.Void) 
       } 
      } 
     } .Else { 
      $output = .Call System.String.Concat(
       $output, 
       .Call Test.SomeClass.PrintNotExists(
        $text, 
        $world, 
        $player)) 
     }; 
     $output 
    } 
} 
+0

코드에서 'SomeClass'는 정적이라고 가정했습니다. 나는 그것을 블록 내에서 선언되지 않은 인스턴스 변수로 표현하는 좋은 방법을 생각할 수 없었다. 그러나 당신의 필요에 맞게 조절할 수 있어야합니다. –

+0

p.s. 여기서'Expression.Condition'을 사용하지 마십시오. 조건 연산자'? :'(일명 "삼항 연산자")에 해당합니다. 실제로 코드에있는 것과 다릅니다. –

+0

나는 원래의 클래스로 할 수있는 것을 제한하기 때문에 methodinfo를 피해 가려고했다. 직접적인 methodinfo보다 람다를 사용하여 추가로 유연성을 확보하려면 더 복잡한 예제가 필요합니다. – bigjokerfish