首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将Lambda表达式计算为表达式树的一部分

将Lambda表达式计算为表达式树的一部分
EN

Stack Overflow用户
提问于 2013-11-26 10:49:36
回答 2查看 2.5K关注 0票数 5

我正在尝试使用表达式树构建lambda表达式。这是我要创建的lambda表达式的格式:

代码语言:javascript
复制
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();
    };

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

代码语言:javascript
复制
Expression<Func<DateTime, string>> formattingExpression = dt => dt.ToShortDateString();

我知道在表达式树之外我可以调用

代码语言:javascript
复制
formattingExpression.Compile()(new DateTime(2003, 2, 1)) 

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

到目前为止,我想出的任何东西似乎都做不到--几乎可以肯定的是,我误解了表达式树是如何工作的。任何帮助都非常感谢!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-11-26 11:28:37

因此,如果我正确地理解了您,您希望创建一个lambda (表达式),它使用您传递的函数并围绕它做一些额外的工作。所以实际上,您只想在表达式的内部使用这个函数。

现在,请允许我建议您甚至不使用表达式。您只需创建一个接受Func<DateTime, string>参数的函数,并使用该参数来处理某些内容。但是,如果你真的需要表达什么东西,我将尝试解释如何构建一个。

对于这个例子,我将创建这个函数:

代码语言:javascript
复制
string MonthAndDayToString (int month, int day)
{
    return "'" + formattingFunction(new DateTime(2013, month, day)) + "'"
}

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

代码语言:javascript
复制
Func<DateTime, string> formatting = dt => (...) // as above

// define parameters for the lambda expression
ParameterExpression monthParam = Expression.Parameter(typeof(int));
ParameterExpression dayParam = Expression.Parameter(typeof(int));

// look up DateTime constructor
ConstructorInfo ci = typeof(DateTime).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int) });

// look up string.Concat
MethodInfo concat = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string), typeof(string) });

// inner call: formatting(new DateTime(2013, month, day))
var call = Expression.Call(formatting.Method, Expression.New(ci, Expression.Constant(2013), monthParam, dayParam));

// concat: "'" + call + "'"
var expr = Expression.Call(concat, Expression.Constant("'"), call, Expression.Constant("'"));

// create the final lambda: (int, int) => expr
var lambda = Expression.Lambda<Func<int, int, string>>(expr, new ParameterExpression[] { monthParam, dayParam });

// compile and execute
Func<int, int, string> func = lambda.Compile();
Console.WriteLine(func(2, 1)); // '01.02.2013 Hello!'
Console.WriteLine(func(11, 26)); // '26.11.2013 Hello!'

在看了亚历克斯的答案后,我似乎误解了你的问题,并试图解决你所做的事情的反面。但是把它改变成你真正想要做的事情并没有太大不同:

代码语言:javascript
复制
Func<DateTime, string> formatting = dt => dt.ToShortDateString();

ParameterExpression param = Expression.Parameter(typeof(DateTime));
MethodInfo concat = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string), typeof(string) });

var call = Expression.Call(formatting.Method, param);
var expr = Expression.Call(concat, Expression.Constant("'"), call, Expression.Constant(" Hello!'"));
var lambda = Expression.Lambda<Func<DateTime, string>>(expr, new ParameterExpression[] { param });

Func<DateTime, string> func = lambda.Compile();
Console.WriteLine(func(new DateTime(2013, 02, 01)));
Console.WriteLine(func(new DateTime(2013, 11, 26)));

但我仍然认为,使用Func<DateTime, string>DateTime参数的正常函数更容易维护。所以除非你真的需要那些表达方式,否则不要使用它们。

为什么我还是不认为你真的需要表达。考虑一下这个例子:

代码语言:javascript
复制
private Func<DateTime, string> formatting = dt => dt.ToShortDateString();
private Func<DateTime, string> formattingLogic = null;

public Func<DateTime, string> FormattingLogic
{
    get
    {
        if (formattingLogic == null)
        {
            // some results from reflection
            string word = "Hello";
            string quote = "'";

            formattingLogic = dt =>
            {
                StringBuilder str = new StringBuilder(quote);
                str.Append(formatting(dt));

                if (!string.IsNullOrWhiteSpace(word))
                    str.Append(" ").Append(word);

                str.Append(quote);
                return str.ToString();
            };
        }

        return formattingLogic;
    }
}

void Main()
{
    Console.WriteLine(FormattingLogic(new DateTime(2013, 02, 01))); // '01.02.2013 Hello'
    Console.WriteLine(FormattingLogic(new DateTime(2013, 11, 26))); // '26.11.2013 Hello'
}

正如您所看到的,我只构建了一次格式化逻辑函数,当它还没有设置时,我会懒洋洋地构造它。这时,反射就会运行,以获取函数中某个位置使用的值。由于函数是作为lambda函数创建的,因此我们从lambda函数的本地作用域中使用的变量将被自动捕获并保持可用。

当然,您也可以将其作为表达式创建,并存储编译后的函数,但我认为这样做更容易阅读和维护。

票数 3
EN

Stack Overflow用户

发布于 2013-11-26 11:22:12

你可以使用:

代码语言:javascript
复制
Func<Func<DateTime,string>, DateTime, string> requiredLambda = (f, dt) =>
{
    var formattedDate = f.Invoke(dt);

    /**
    * 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();
};

然后,您就有了输入表达式:

代码语言:javascript
复制
Expression<Func<DateTime, string>> formattingExpression = 
    dt => dt.ToShortDateString();

其结果是:

代码语言:javascript
复制
var result = requiredLambda
    .Invoke(formattingExpression.Compile(), new DateTime(2003, 2, 1));

// 1.2.2003 Hello!
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20214965

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档