c# - 从 Expression<Func<IMyclass, int>> 创建 setter Expression<Action<object, object>>

标签 c# lambda expression-trees

我有一个Expression<Func<IMyclass, int>>我想将其转换为输入为 Expression<Action<object, object>> 的 setter 。如何才能实现这一目标?

问题是object范围。使用正确的类型( string ),这会很容易。

class Foo
{
    public Bar Bar { get; set; }
}
class Bar
{
    public string Baz { get; set; }    
}

static void Main(string[] args)
{
    var expr = GetExpression(t => t.Bar.Baz);
    var member = expr.Body as MemberExpression;

    var p = Expression.Parameter(typeof(object), "p");
    // This does not work...
    var assign = Expression.Assign(member, p);
    var lambda = Expression.Lambda<Action<object, object>>(assign, p);

    object o = new Foo();
    object v = "test";
    lambda.Compile().Invoke(o, v);
}

private static Expression<Func<Foo, string>> GetExpression(Expression<Func<Foo, string>> expr)
{
    return expr;
}

最佳答案

这是可能的,但并非微不足道。首先,您需要重写原始表达式。现在它具有以下形式:

(Foo t) => t.Bar.Baz;

你需要它是这样的:

(object t) => ((Foo)t).Bar.Baz;

所以你需要表达式访问者:

private class ReplaceParameterExpressionVisitor : System.Linq.Expressions.ExpressionVisitor {
    private readonly ParameterExpression _toReplace;
    private readonly ParameterExpression _replaceWith;
    public ReplaceParameterExpressionVisitor(ParameterExpression toReplace, ParameterExpression replaceWith) {
        _toReplace = toReplace;
        _replaceWith = replaceWith;
    }            

    protected override Expression VisitParameter(ParameterExpression node) {
        if (node == _toReplace)
            // replace with new parameter and convert to the old parameter type
            return Expression.Convert(_replaceWith, _toReplace.Type);
        return base.VisitParameter(node);
    }
}

然后你的代码变成:

static void Main(string[] args)
{
    var expr = GetExpression(t => t.Bar.Baz);
    var member = expr.Body as MemberExpression;

    // define new parameter of type object
    var target = Expression.Parameter(typeof(object), "t");
    var value =  Expression.Parameter(typeof(object), "p");
    // replace old parameter of type Foo to new one
    member = (MemberExpression) new ReplaceParameterExpressionVisitor(expr.Parameters[0], target).Visit(member);
    // convert value to target type, because you cannot assign object to string
    var assign = Expression.Assign(member, Expression.Convert(value, member.Type));
    // now we have (target, value) => ((Foo)target).Bar.Baz = (string) value;
    var lambda = Expression.Lambda<Action<object, object>>(assign, target, value);

    var o = new Foo();
    // set bar or will throw null reference
    o.Bar = new Bar();
    object v = "test";
    lambda.Compile().Invoke(o, v);
}

关于c# - 从 Expression<Func<IMyclass, int>> 创建 setter Expression<Action<object, object>>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43409713/

相关文章:

c++ - 为什么不能在没有可变参数的情况下在 lambda 内部转发参数?

java - CompletableFuture.runAsync(() ->... 带有非最终变量

c# - 协方差和 orderby() 问题

c# - 卡在 C# 中的 BinaryExpression

linq - 执行 LambdaExpression 的问题

c# - 为什么 BCrypt.net GenerateSalt(31) 会立即返回?

c# - .NET 如何确定字符的 Unicode 类别?

c# - 一点点弦乐 Actor

c# - c#中的多个条件

lambda - 如何将参数传递给 Java 8 Lambda 流函数?