从另外两个表达式创建动态 lambda(链接表达式)

发布于 2024-12-11 13:25:13 字数 621 浏览 0 评论 0 原文

给定一个接受识别对象并返回属性的 lambda:

Expression<Func<Identification, object>> fx = _ => _.Id;

以及将对象转换为识别实例的转换 lambda:

ParameterExpression p = Expression.Parameter(typeof(object), "o");
Expression @new = Expression.Lambda(Expression.Convert(p, typeof(Identification)), p);

如何构建一个执行 @new 的新 lambda(获取识别)实例)并将结果传递到fx。我需要 @new 的结果以某种方式绑定到 fx 的第一个参数,但我找不到示例。

我需要结果为 Expression,它的类型应为 Expression> 并且应将入站参数转换为 识别,然后获取Id属性。

Given a lambda that takes an Identification object, and returns a property:

Expression<Func<Identification, object>> fx = _ => _.Id;

And a conversion lambda that converts an object into an Identification instance:

ParameterExpression p = Expression.Parameter(typeof(object), "o");
Expression @new = Expression.Lambda(Expression.Convert(p, typeof(Identification)), p);

How do I build a new lambda that executes @new (getting out the Identification Instance) and passes the result into fx. I need @new's result to bind to the first parameter of fx somehow, and I cannot find an example.

I need the result to be an Expression, it should be of type Expression<Func<object, object>> and it should convert the inbound parameter to an Identification and then get the Id property.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

最单纯的乌龟 2024-12-18 13:25:13

首先,请注意,如果您正确键入 @new,这会更容易,即:

LambdaExpression @new = ...

因为这样可以轻松访问 @new.Body@new.Parameters;做到了,
Expression.Invoke 在这里很有用:

var combinedParam = Expression.Parameter(typeof(object), "o");
var combined = Expression.Lambda<Func<object, object>>(
    Expression.Invoke(fx,
        Expression.Invoke(@new, combinedParam)), combinedParam);

尽管对于更清晰的表达式,您也可以使用 ExpressionVisitor 完全替换内部表达式:

var injected = new SwapVisitor(fx.Parameters[0], @new.Body).Visit(fx.Body);
var combined = Expression.Lambda<Func<object, object>>(injected,@new.Parameters);

with:

class SwapVisitor : ExpressionVisitor {
    private readonly Expression from, to;
    public SwapVisitor(Expression from, Expression to) {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node) {
        return node == from ? to : base.Visit(node);
    }
}

这样做的作用是:

  • 检查 < code>fx.Body 树,用 @new.Body 替换 _ (参数)的所有实例(请注意,这将包含对 _ 的引用) code>o 参数(又名p
  • 然后,我们从替换的表达式构建一个新的 lambda,重新使用 @new 中的相同参数,这确保我们注入的值将被正确绑定

Firstly, note that this is easier if you type @new appropriately, i.e.:

LambdaExpression @new = ...

since that provides easy access to @new.Body and @new.Parameters; that done,
Expression.Invoke can be useful here:

var combinedParam = Expression.Parameter(typeof(object), "o");
var combined = Expression.Lambda<Func<object, object>>(
    Expression.Invoke(fx,
        Expression.Invoke(@new, combinedParam)), combinedParam);

although for a cleaner expression, you can also use ExpressionVisitor to completely replace the inner expressions:

var injected = new SwapVisitor(fx.Parameters[0], @new.Body).Visit(fx.Body);
var combined = Expression.Lambda<Func<object, object>>(injected,@new.Parameters);

with:

class SwapVisitor : ExpressionVisitor {
    private readonly Expression from, to;
    public SwapVisitor(Expression from, Expression to) {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node) {
        return node == from ? to : base.Visit(node);
    }
}

what this does is:

  • inspect the fx.Body tree, replacing all instances of _ (the parameter) with the @new.Body (note that this will contain references to the o parameter (aka p)
  • we then build a new lambda from the replaced expression, re-using the same parameters from @new, which ensures that the values we injected will be bound correctly
ゞ记忆︶ㄣ 2024-12-18 13:25:13

使用 Marc Gravell 的答案中的代码,您可以通过辅助函数很好地简化此操作:

public static class ExpressionHelper {
    public static Expression<Func<TFrom, TTo>> Chain<TFrom, TMiddle, TTo>(
        this Expression<Func<TFrom, TMiddle>> first,
        Expression<Func<TMiddle, TTo>> second
    ) {
        return Expression.Lambda<Func<TFrom, TTo>>(
           new SwapVisitor(second.Parameters[0], first.Body).Visit(second.Body),
           first.Parameters
        );
    }

    private class SwapVisitor : ExpressionVisitor {
        private readonly Expression _from;
        private readonly Expression _to;

        public SwapVisitor(Expression from, Expression to) {
            _from = from;
            _to = to;
        }

        public override Expression Visit(Expression node) {
            return node == _from ? _to : base.Visit(node);
        }
    }
}

现在看看它有多干净!

var valueSelector = new Expression<Func<MyTable, int>>(o => o.Value);
var intSelector = new Expression<Func<int, bool>>(x => x > 5);
var selector = valueSelector.Chain<MyTable, int, bool>(intSelector);

它可以与实体框架和其他需要干净的Expression的东西一起使用,而不会尝试调用其中的Func

Using the code from Marc Gravell's answer, you can simplify this really nicely with a helper function:

public static class ExpressionHelper {
    public static Expression<Func<TFrom, TTo>> Chain<TFrom, TMiddle, TTo>(
        this Expression<Func<TFrom, TMiddle>> first,
        Expression<Func<TMiddle, TTo>> second
    ) {
        return Expression.Lambda<Func<TFrom, TTo>>(
           new SwapVisitor(second.Parameters[0], first.Body).Visit(second.Body),
           first.Parameters
        );
    }

    private class SwapVisitor : ExpressionVisitor {
        private readonly Expression _from;
        private readonly Expression _to;

        public SwapVisitor(Expression from, Expression to) {
            _from = from;
            _to = to;
        }

        public override Expression Visit(Expression node) {
            return node == _from ? _to : base.Visit(node);
        }
    }
}

Now look how clean that is!

var valueSelector = new Expression<Func<MyTable, int>>(o => o.Value);
var intSelector = new Expression<Func<int, bool>>(x => x > 5);
var selector = valueSelector.Chain<MyTable, int, bool>(intSelector);

And it works with Entity Framework and other things that need a clean Expression that doesn't try to invoke a Func within it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文