评估Lambda表达式作为表达式树的一部分

Law*_*nce 5 c# lambda expression-trees

我正在尝试使用表达式树构建一个lambda表达式.这是我试图创建的lambda表达式的格式:

Func<DateTime, string> requiredLambda = dt =>
    {
        var formattedDate = dt.ToShortDateString();

        /**
        * The logic is not important here - what is important is that I 
        * am using the result of the executed lambda expression.
        * */
        var builder = new StringBuilder();
        builder.Append(formattedDate);
        builder.Append(" Hello!");
        return builder.ToString();
    };
Run Code Online (Sandbox Code Playgroud)

问题是我不是从头开始构建这个树 - 格式化逻辑已经以一个实例的形式传递给我Expression<Func<DateTime, string>>- 比如说:

Expression<Func<DateTime, string>> formattingExpression = dt => dt.ToShortDateString();
Run Code Online (Sandbox Code Playgroud)

我知道外面的表达式树的我可以打电话

formattingExpression.Compile()(new DateTime(2003, 2, 1)) 
Run Code Online (Sandbox Code Playgroud)

评估表达式 - 但问题是我希望表达式树中评估和分配它- 允许我在表达式树中对结果执行额外的逻辑.

到目前为止,我没有想到的任何事情似乎都是这样做的 - 几乎可以肯定是对表达树如何工作的误解.任何帮助非常感谢!

pok*_*oke 3

因此,如果我理解正确的话,您想要创建一个 lambda(表达式),它使用您传递的函数并围绕它做一些额外的工作。所以你本质上只想在表达式中使用这个函数。

\n\n

此时,请允许我建议您甚至不要使用表达式。你可以创建一个函数,它需要一个Func<DateTime, string>参数并使用该参数来处理某些内容的函数。但如果你真的需要某些东西的表达式,我\xe2\x80\x99ll只是尝试解释如何构建一个。

\n\n

对于这个例子,我\xe2\x80\x99将创建这个函数:

\n\n
string MonthAndDayToString (int month, int day)\n{\n    return "\'" + formattingFunction(new DateTime(2013, month, day)) + "\'"\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

如您所见, I\xe2\x80\x99m 将创建一个,Func<int, int, string>然后创建DateTime对象并将其传递给函数,然后进一步更改结果。

\n\n
Func<DateTime, string> formatting = dt => (...) // as above\n\n// define parameters for the lambda expression\nParameterExpression monthParam = Expression.Parameter(typeof(int));\nParameterExpression dayParam = Expression.Parameter(typeof(int));\n\n// look up DateTime constructor\nConstructorInfo ci = typeof(DateTime).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int) });\n\n// look up string.Concat\nMethodInfo concat = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string), typeof(string) });\n\n// inner call: formatting(new DateTime(2013, month, day))\nvar call = Expression.Call(formatting.Method, Expression.New(ci, Expression.Constant(2013), monthParam, dayParam));\n\n// concat: "\'" + call + "\'"\nvar expr = Expression.Call(concat, Expression.Constant("\'"), call, Expression.Constant("\'"));\n\n// create the final lambda: (int, int) => expr\nvar lambda = Expression.Lambda<Func<int, int, string>>(expr, new ParameterExpression[] { monthParam, dayParam });\n\n// compile and execute\nFunc<int, int, string> func = lambda.Compile();\nConsole.WriteLine(func(2, 1)); // \'01.02.2013 Hello!\'\nConsole.WriteLine(func(11, 26)); // \'26.11.2013 Hello!\'\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

看了 Alex\xe2\x80\x99 的答案后,我似乎误解了你的问题,并试图解决你正在做的事情的相反问题。但它\xe2\x80\x99s并没有太大的不同,无法将其更改为您实际想要做的事情:

\n\n
Func<DateTime, string> formatting = dt => dt.ToShortDateString();\n\nParameterExpression param = Expression.Parameter(typeof(DateTime));\nMethodInfo concat = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string), typeof(string) });\n\nvar call = Expression.Call(formatting.Method, param);\nvar expr = Expression.Call(concat, Expression.Constant("\'"), call, Expression.Constant(" Hello!\'"));\nvar lambda = Expression.Lambda<Func<DateTime, string>>(expr, new ParameterExpression[] { param });\n\nFunc<DateTime, string> func = lambda.Compile();\nConsole.WriteLine(func(new DateTime(2013, 02, 01)));\nConsole.WriteLine(func(new DateTime(2013, 11, 26)));\n
Run Code Online (Sandbox Code Playgroud)\n\n

Func<DateTime, string>但我\xe2\x80\x99d 仍然认为采用 a和 a参数的普通函数DateTime会更容易维护。所以除非你真的需要这些表达方式,否则请避免使用它们。

\n\n
\n\n

为什么我仍然不\xe2\x80\x99t认为你真的需要表达这个。考虑这个例子:

\n\n
private Func<DateTime, string> formatting = dt => dt.ToShortDateString();\nprivate Func<DateTime, string> formattingLogic = null;\n\npublic Func<DateTime, string> FormattingLogic\n{\n    get\n    {\n        if (formattingLogic == null)\n        {\n            // some results from reflection\n            string word = "Hello";\n            string quote = "\'";\n\n            formattingLogic = dt =>\n            {\n                StringBuilder str = new StringBuilder(quote);\n                str.Append(formatting(dt));\n\n                if (!string.IsNullOrWhiteSpace(word))\n                    str.Append(" ").Append(word);\n\n                str.Append(quote);\n                return str.ToString();\n            };\n        }\n\n        return formattingLogic;\n    }\n}\n\nvoid Main()\n{\n    Console.WriteLine(FormattingLogic(new DateTime(2013, 02, 01))); // \'01.02.2013 Hello\'\n    Console.WriteLine(FormattingLogic(new DateTime(2013, 11, 26))); // \'26.11.2013 Hello\'\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

正如你所看到的,我\xe2\x80\x99m只构造了一次格式化逻辑函数,当它\xe2\x80\x99s尚未设置时,它是懒惰的。当反射运行以获取您在函数中某处使用的某些值时,\xe2\x80\x99s 。由于该函数是作为 lambda 函数创建的,因此我们在 lambda 函数内的局部作用域中使用的变量会自动捕获并保持可用。

\n\n

当然,现在您也可以将其创建为表达式并存储编译后的函数,但我\xe2\x80\x99d 认为这样做更具可读性和可维护性。

\n