4

LambdaExpression を文字列に変換するメソッドがあります。これらの文字列をキャッシュのキーとして使用します。

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

これとは異なります:

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

ただし、現在の LambdaToString メソッドの状態では、p の値に関係なく同じ出力が生成されます。それは次のとおりです。

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

LambdaToString 関数で実行したいのは、式の "value(class).p" 部分を、場合に応じて "x" または "y" の実際のリテラル文字列に解決することです。

これが私の 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;
    }
4

4 に答える 4

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 + constantでキーを作成してみませんか?サンプルコードでは、キーは次のようになります。

"m => m.P == p @@SPECIAL_SEPERATOR@@ x"
"m => m.P == p @@SPECIAL_SEPERATOR@@ y"

はい、1つの定数に「@@ SPECIAL_SEPERATOR @@」のような値が含まれている場合、すべてがクラッシュします。キャッシュキーとして文字列を選択するため、これは最初から厳密な解決策ではありません。

別のキャッシュアプ​​ローチを選択する場合は、これを確認してください。

于 2013-03-18T15:27:51.863 に答える
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();

注意

もちろん、これは多くのシナリオには当てはまりません。値を取得する方法を理解するための基本にすぎません。あなたの述語が

var x = 1;
var y = 2;
var result = LambdaToString<YourType>(v => v.A== x && v.B == y)
于 2013-03-18T14:35:58.710 に答える
2

これが私の答えです。理想的には、これは、スローされた可能性のある式を処理できるようになります。今のところそうではない可能性が高いですが、私がテストで投げた単純で一般的なことはすべて処理しました。

これで処理できない例を思いついた場合は、コメントに残してください。それを処理するように関数を変更しようとします。

    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;
        }
于 2013-03-18T15:10:48.960 に答える
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;
        }
于 2013-03-18T14:47:40.160 に答える