C# 如何结合表达<;Func<;T>&燃气轮机;和表达<;Func<;T、 浮动>&燃气轮机;表达<;Func<;浮动>>;?

C# 如何结合表达<;Func<;T>&燃气轮机;和表达<;Func<;T、 浮动>&燃气轮机;表达<;Func<;浮动>>;?,c#,lambda,linq-expressions,C#,Lambda,Linq Expressions,我正在尝试组合expr1和expr2以生成表达式: 问题是: 将expr3设置为MemberExpression而不是LambdaExpression的正确方法是什么 我正在努力实现的目标: 我想把像()=>\u modelRgb.R这样的表达式传递给一个方法,它指向一个ColorComponent,在这个方法中,我想为它的一些成员构建大量的表达式。基本上,您要做的是组合两个表达式。这是一个解决方案,展示了如何做到这一点,尽管它需要一些修改,以便使第一个表达式没有参数,而不是一个参数 调整后的C

我正在尝试组合
expr1
expr2
以生成
表达式

问题是:

expr3
设置为
MemberExpression
而不是
LambdaExpression
的正确方法是什么

我正在努力实现的目标


我想把像
()=>\u modelRgb.R
这样的表达式传递给一个方法,它指向一个
ColorComponent
,在这个方法中,我想为它的一些成员构建大量的表达式。

基本上,您要做的是组合两个表达式。这是一个解决方案,展示了如何做到这一点,尽管它需要一些修改,以便使第一个表达式没有参数,而不是一个参数

调整后的
Compose
方法如下所示:

public static Expression<Func<TResult>> Compose<TSource, TResult>(
    this Expression<Func<TSource>> first,
    Expression<Func<TSource, TResult>> second)
{
    return Expression.Lambda<Func<TResult>>(
        second.Body.Replace(second.Parameters[0], first.Body));
}

通过使用上述方法对代码进行泛化,您可以确保无论表达式的内容如何,代码都能正常工作,而不是编写一个对表达式中可以或不可以包含的内容进行假设的方法,或者以不同的方式处理一系列不同的情况。

这里您基本上要做的是组合两个表达式。这是一个解决方案,展示了如何做到这一点,尽管它需要一些修改,以便使第一个表达式没有参数,而不是一个参数

调整后的
Compose
方法如下所示:

public static Expression<Func<TResult>> Compose<TSource, TResult>(
    this Expression<Func<TSource>> first,
    Expression<Func<TSource, TResult>> second)
{
    return Expression.Lambda<Func<TResult>>(
        second.Body.Replace(second.Parameters[0], first.Body));
}

通过使用上述方法对代码进行泛化,您可以确保无论表达式的内容如何,代码都能正常工作,而不是编写一个对表达式中可以或不可以包含的内容进行假设的方法,或者以不同的方式处理一系列不同的案例。

在原始海报的特定案例中,这看起来是正确的;它将产生
()=>\u modelRgb.R.Value
。但对于更复杂的情况,存在一些问题;像这样的beta缩减替换只在没有副作用的语言中有效。例如,如果您想将
()=>M()
s=>N(s,s)
组合在一起,那么您直接的beta缩减到
()=>N(M(),M())
与更复杂的
()=>(s=>N(s,s))((()=>M())
具有不同的语义。前者调用
M()
两次;后者只叫它一次;如果
M()
不是幂等的,则这一点很重要。@EricLippert True,尽管很少看到在相关情况下使用表达式(拥有表达式的主要原因是有代码检查表达式,而不是简单地编译并作为C#代码运行;如果他们只想编译并运行代码,他们可以简单地将操作作为编译后的表达式准确组合,只需在组合表达式之前编译表达式即可)。类似地,几乎所有检查这些表达式的代码(最明显的是试图将代码转换为SQL的查询提供程序)都无法理解任何形式的翻译,而这些翻译没有您提到的问题(即,将结果存储在变量中,然后使用该变量),所以你真的没有什么可以做的。但完全正确的是,在一些不常见的情况下,理解这一点很重要。感谢所有这些解释,我真的很想更好地理解表达式。问题是,MSDN文档中几乎没有示例,这对我来说非常重要“这是一个很难接近的主题。@Aybe:我很抱歉。在设计、实现和测试该功能时,我是一个适当规范和更多MSDN文档的倡导者,但遗憾的是,我没有按自己的方式行事。在随后的十年中有了改进,但仍然可以做得更好。在原始海报这看起来是正确的;它会产生
()=>\u modelRgb.R.Value
。但对于更复杂的情况,会有问题;像这样的beta缩减替换只在没有副作用的语言中有效。例如,如果您想将
()=>M()
s=>N(s,s)
那么您直接的beta版缩减为
()=>N(M(),M())
与更复杂的
()=>(s=>N(s,s))((()=>M())())
有不同的语义。前者调用
M()
两次;后者只调用一次;如果
M(),这很重要
不是幂等的。@EricLippert为True,尽管很少看到在相关情况下使用表达式(拥有表达式的主要原因是有代码检查表达式,而不是简单地编译并作为C#代码运行;如果他们只想编译并运行代码,他们可以简单地将操作作为编译后的表达式准确组合,只需在组合表达式之前编译表达式即可)。类似地,几乎所有检查这些表达式的代码(最明显的是试图将代码转换为SQL的查询提供程序)都无法理解任何形式的翻译,而这些翻译没有您提到的问题(即,将结果存储在变量中,然后使用该变量),所以你真的没有什么可以做的。但完全正确的是,在一些不常见的情况下,理解这一点很重要。感谢所有这些解释,我真的很想更好地理解表达式。问题是,MSDN文档中几乎没有示例,这对我来说非常重要“这是一个很难接近的主题。@Aybe:我很抱歉。在设计、实现和测试该功能时,我是一个适当规范和更多MSDN文档的倡导者,但遗憾的是,我没有按自己的方式行事。在随后的十年中,已经有了改进,但它仍然可以做得更好。”。
public static Expression<Func<TResult>> Compose<TSource, TResult>(
    this Expression<Func<TSource>> first,
    Expression<Func<TSource, TResult>> second)
{
    return Expression.Lambda<Func<TResult>>(
        second.Body.Replace(second.Parameters[0], first.Body));
}
public class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }

    public override Expression Visit(Expression ex)
    {
        if (ex == from) return to;
        else return base.Visit(ex);
    }
}
public static Expression Replace(this Expression ex,
    Expression from,
    Expression to)
{
    return new ReplaceVisitor(from, to).Visit(ex);
}