如何将一种类型的表达式树转换为不同的表达式类型?

发布于 2024-11-24 02:23:32 字数 831 浏览 1 评论 0 原文

如果我有两个几乎相同的类 AnimalAnimalViewModel 以及与视图模型相关的表达式树,我如何将其转换为 Animal

public class Animal
{
   public string Species { get; set; }
   public string Name { get; set; }
   public string Sound { get; set; }
}
public class AnimalViewModel : ViewModelBase
{
   public string Species { get; set; }
   public string Name { get; set; }
   public string Sound { get; set; }
}

如何将 Expression> 转换为 Expression>

public static Expression<Func<Animal,bool>> Translate (Expression<Func<AnimalViewModel,bool>> expression)
{
  // What goes here?  I assume I have to traverse the tree somehow.
}

If I have two nearly identical classes Animal and AnimalViewModel and an expression tree related to the viewmodel, how can I translate it to Animal?

public class Animal
{
   public string Species { get; set; }
   public string Name { get; set; }
   public string Sound { get; set; }
}
public class AnimalViewModel : ViewModelBase
{
   public string Species { get; set; }
   public string Name { get; set; }
   public string Sound { get; set; }
}

How can I translate an Expression<Func<AnimalViewModel,bool>> to Expression<Func<Animal,bool>>?

public static Expression<Func<Animal,bool>> Translate (Expression<Func<AnimalViewModel,bool>> expression)
{
  // What goes here?  I assume I have to traverse the tree somehow.
}

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

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

发布评论

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

评论(2

暗喜 2024-12-01 02:23:32

这是一位完成这项工作的访客。

  • 它创建参数的副本(因为我们需要创建一个新参数并用旧参数的所有引用替换新参数)
  • 它遍历树的 .Body,替换参数,并将针对 old 类型的任何成员访问切换为 new 类型上的类似命名成员,
  • 它使用我们发明的参数重新组装 lambda 早期

class TypeChangeVisitor : ExpressionVisitor
{
    private readonly Type from, to;
    private readonly Dictionary<Expression, Expression> substitutions;
    public TypeChangeVisitor(Type from, Type to, Dictionary<Expression, Expression> substitutions)
    {
        this.from = from;
        this.to = to;
        this.substitutions = substitutions;
    }
    public override Expression  Visit(Expression node)
    { // general substitutions (for example, parameter swaps)
        Expression found;
        if(substitutions != null && substitutions.TryGetValue(node, out found))
        {
            return found;
        }
        return base.Visit(node);
    }
    protected override Expression VisitMember(MemberExpression node)
    { // if we see x.Name on the old type, substitute for new type
        if (node.Member.DeclaringType == from)
        {
            return Expression.MakeMemberAccess(Visit(node.Expression),
                to.GetMember(node.Member.Name, node.Member.MemberType,
                BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Single());
        }
        return base.VisitMember(node);
    }
}
public class Program
{
    public static void Main()
    {
        Expression<Func<AnimalViewModel, bool>> predicate = x => x.Name == "abc";
        var switched = Translate<AnimalViewModel, Animal>(predicate);
    }
    public static Expression<Func<TTo, bool>> Translate<TFrom, TTo>(Expression<Func<TFrom, bool>> expression)
    {
        var param = Expression.Parameter(typeof(TTo), expression.Parameters[0].Name);
        var subst = new Dictionary<Expression, Expression> { { expression.Parameters[0], param } };
        var visitor = new TypeChangeVisitor(typeof(TFrom), typeof(TTo), subst);
        return Expression.Lambda<Func<TTo, bool>>(visitor.Visit(expression.Body), param);
    }
}

代码 如果你有x.Something.Name 你可能需要更加小心,但这应该会给你一个合理的方法。

Here's a visitor that does the job.

  • it makes a copy of the parameter (since we'll need to create a new parameter and substitute all references of the old parameter for the new one)
  • it walks the .Body of the tree, substituting the parameter, and switching any member-access against the old type to a like-named member on the new type
  • it re-assembles a lambda using the parameter we invented earler

Code:

class TypeChangeVisitor : ExpressionVisitor
{
    private readonly Type from, to;
    private readonly Dictionary<Expression, Expression> substitutions;
    public TypeChangeVisitor(Type from, Type to, Dictionary<Expression, Expression> substitutions)
    {
        this.from = from;
        this.to = to;
        this.substitutions = substitutions;
    }
    public override Expression  Visit(Expression node)
    { // general substitutions (for example, parameter swaps)
        Expression found;
        if(substitutions != null && substitutions.TryGetValue(node, out found))
        {
            return found;
        }
        return base.Visit(node);
    }
    protected override Expression VisitMember(MemberExpression node)
    { // if we see x.Name on the old type, substitute for new type
        if (node.Member.DeclaringType == from)
        {
            return Expression.MakeMemberAccess(Visit(node.Expression),
                to.GetMember(node.Member.Name, node.Member.MemberType,
                BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Single());
        }
        return base.VisitMember(node);
    }
}
public class Program
{
    public static void Main()
    {
        Expression<Func<AnimalViewModel, bool>> predicate = x => x.Name == "abc";
        var switched = Translate<AnimalViewModel, Animal>(predicate);
    }
    public static Expression<Func<TTo, bool>> Translate<TFrom, TTo>(Expression<Func<TFrom, bool>> expression)
    {
        var param = Expression.Parameter(typeof(TTo), expression.Parameters[0].Name);
        var subst = new Dictionary<Expression, Expression> { { expression.Parameters[0], param } };
        var visitor = new TypeChangeVisitor(typeof(TFrom), typeof(TTo), subst);
        return Expression.Lambda<Func<TTo, bool>>(visitor.Visit(expression.Body), param);
    }
}

Note that if you have x.Something.Name you might need to be a bit more careful, but this should get you a reasonable way.

将军与妓 2024-12-01 02:23:32

您是否尝试过 Automapper 来处理类似的事情?

Have you tried Automapper for something like this?

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