2013-03-18 2 views
3

LambdaExpression을 문자열로 변환하는 메서드가 있습니다. 이러한 문자열을 캐시의 키로 사용합니다.값을 포함한 LambdaExpression을 문자열로 변환

string p = "x"; 
var a = LambdaToString<MyType>(m => m.P == p); 

이 다르다 그러나

string p = "y"; 
var a = LambdaToString<MyType>(m => m.P == p); 

관계없이, P의 값과 동일한 출력을 생성한다 내 LambdaToString 방법의 현재 상태. 어느입니다 : 내가 내 LambdaToString 기능을 원하는 무엇

(MyType.P == value(ConsoleApplication1.Program+<>c__DisplayClass0).p) 

는 등의 "X"또는 "Y"의 실제 리터럴 문자열로 표현의 "값 (클래스) .P"부분을 해결하는 것입니다 경우가있을 수 있습니다.

여기 내 LambdaToString 메서드의 현재 상태입니다. 나는 내가 원하는 출력 생산을 수정하려면 어떻게해야 될지 확실하지 않다 :

public static string LambdaToString<T>(Expression<Func<T, bool>> expression) 
    { 
     string body = expression.Body.ToString(); 

     foreach (var parm in expression.Parameters) 
     { 
      var parmName = parm.Name; 
      var parmTypeName = parm.Type.Name; 
      body = body.Replace(parmName + ".", parmTypeName + "."); 
     } 

     return body; 
    } 
+0

물론'p'가'const' 변수 인 경우 이것은 자동으로 발생합니다 (변수는'

답변

1

내 대답은 다음과 같습니다. 이상적으로는이 메소드는 가능한 표현식을 처리 할 수 ​​있어야합니다. 지금 당장은 그렇지 않을 가능성이 있지만, 테스트에서 던져 넣은 모든 단순하고 일반적인 것들을 다뤘습니다.

이 문제를 해결하지 못하는 예제가 있으면 의견에 남겨두면 함수를 수정하여 처리하도록 노력할 것입니다.

public static string LambdaToString<T>(Expression<Func<T, bool>> expression) 
    { 

     var replacements = new Dictionary<string, string>(); 
     WalkExpression(replacements, expression); 


     string body = expression.Body.ToString(); 

     foreach (var parm in expression.Parameters) 
     { 
      var parmName = parm.Name; 
      var parmTypeName = parm.Type.Name; 
      body = body.Replace(parmName + ".", parmTypeName + "."); 
     } 

     foreach (var replacement in replacements) 
     { 
      body = body.Replace(replacement.Key, replacement.Value);  
     } 

     return body; 
    } 

    private static void WalkExpression(Dictionary<string, string> replacements, Expression expression) 
    { 
     switch (expression.NodeType) 
     { 
      case ExpressionType.MemberAccess: 
       string replacementExpression = expression.ToString(); 
       if (replacementExpression.Contains("value(")) 
       { 
        string replacementValue = Expression.Lambda(expression).Compile().DynamicInvoke().ToString(); 
        if (!replacements.ContainsKey(replacementExpression)) 
        { 
         replacements.Add(replacementExpression, replacementValue.ToString()); 
        } 
       } 
       break; 

      case ExpressionType.GreaterThan: 
      case ExpressionType.GreaterThanOrEqual: 
      case ExpressionType.LessThan: 
      case ExpressionType.LessThanOrEqual: 
      case ExpressionType.OrElse: 
      case ExpressionType.AndAlso: 
      case ExpressionType.Equal: 
       var bexp = expression as BinaryExpression; 
       WalkExpression(replacements, bexp.Left); 
       WalkExpression(replacements, bexp.Right); 
       break; 

      case ExpressionType.Call: 
       var mcexp = expression as MethodCallExpression; 
       foreach (var argument in mcexp.Arguments) 
       { 
        WalkExpression(replacements, argument); 
       } 
       break; 

      case ExpressionType.Lambda: 
       var lexp = expression as LambdaExpression; 
       WalkExpression(replacements, lexp.Body); 
       break; 

      case ExpressionType.Constant: 
       //do nothing 
       break; 

      default: 
       Trace.WriteLine("Unknown type"); 
       break; 
     } 
+0

이름이 같지만 클래스가 다른 두 개의 정적 메서드가있는 시나리오는 처리하지 않습니다. 예 : '() => Class1.StaticM() + Class2.StaticM()'은 StaticM() + StaticM()으로 직렬화됩니다. –

+0

이것은 또한 이상 값보다 작은 입력 값을 해결하기 위해 표현식을 호출하는 것처럼 보입니다. –

2

글쎄, p 값을 얻으려면, 당신이 할 수있는 (이 작업을 수행하는 아마 더 쉽고 강력한 방법을하지만) .

public static string LambdaToString<T>(Expression<Func<T, bool>> expression) 
    { 
     BinaryExpression binaryExpression = expression.Body as BinaryExpression; 
     Expression right = binaryExpression.Right;//right part of the "==" of your predicate 
     var objectMember = Expression.Convert(right, typeof(object));//convert to object, as we don't know what's in 

     var getterLambda = Expression.Lambda<Func<object>>(objectMember); 

     var getter = getterLambda.Compile(); 



     var valueYouWant = getter();//here's the "x" or "y" 
     //... 

또는 물론

Expression right = (expression.Body as BinaryExpression).Right; 
var valueYouWant = Expression.Lambda(right).Compile().DynamicInvoke(); 

주의

짧은,이 scenarii를 많이 맞지 않는, 그것은 가치를 얻을하는 방법을 이해하는 단지 기본입니다. 당신의 술어가

var x = 1; 
var y = 2; 
var result = LambdaToString<YourType>(v => v.A== x && v.B == y) 
1

매우 신속하고 더러운 솔루션은 매개 변수의 이름과 값을 전달하고 바로 교체하는 것입니다 경우 작동하지 않습니다.

public static string LambdaToString<T>(Expression<Func<T, bool>> expression, string value,string paramName) 
     { 

      string body = expression.Body.ToString().Replace(paramName,value); 


      foreach (var parm in expression.Parameters) 
      { 
       var parmName = parm.Name; 

       var parmTypeName = parm.Type.Name; 
       body = body.Replace(parmName + ".", parmTypeName + "."); 
      } 

      return body; 
     } 
3

나는 캐시에 대한 키로 이러한 문자열을 사용합니다.

많은 경우에 잘못된 것이지만 프로젝트에서도 작동합니다. 키가 쉽게 패배 할 수 Expression.ToString() 사용 : 위의

//counter-example 1 
Expression<Func<string, bool>> exp1 = s => s == "a"; 
Expression<Func<string, bool>> exp2 = ss => ss == "a"; 
//the two will be considered different in your cache solution 
//but they are essentially the same, well that's not the worst, see next 

//counter-example 2 
Expression<Func<int, bool>> exp3 = i => i > 10; 
Expression<Func<long, bool>> exp4 = i => i > 10; 
//the two will be considered the same in your cache solution 
//of course they are different, probably hences runtime exceptions 

것은 전혀 응답하지 않습니다. 신경 쓰지 않는다면 "문자열을 키로 사용하기"를 기반으로 계속하겠습니다.

표현식을 캐시하지만 그 안에 상수가있는 동일한 모양의 표현식을 식별하려고합니다. 그렇다면 왜 expression + 상수로 키를 만들지 않겠습니까?하나 개의 상수는 "@@ @@ SPECIAL_SEPERATOR"모든 충돌을 것입니다 같은 값을 포함하는 경우, 예

"m => m.P == p @@[email protected]@ x" 
"m => m.P == p @@[email protected]@ y" 

과 : 귀하의 예제 코드에서 키가 될 것입니다. 이것은 처음부터 엄격한 해결책이 아닙니다. 왜냐하면 문자열을 캐시 키로 선택하기 때문입니다.

다른 캐시 방법을 선택하려면 this을 선택하십시오.

관련 문제