遍历表达式树并提取参数

本文关键字:提取 参数 表达式 遍历 | 更新日期: 2023-09-27 18:03:39

我在写一种映射工具。我有一个方法,看起来像这样(简化):

   public void RegisterMapping<TTarget, TSource>(string propertyName, 
                                                 Expression<Func<TSource, object>> memberMap)

memberMap是定义如何将一个属性从TSource转换为TTarget的表达式。对于业务逻辑,我需要从中提取对TSource属性的所有引用。例如,from

x => x.Customers.Where(c => c.Orders.Any())

我想得到Customers,并从

x => x.FirstName + " " + x.LastName

FirstNameLastName(可以作为string[], PropertyInfo的转换很简单)

我该怎么做呢?我的第一种方法是手动遍历树,检查节点类型并根据节点类型检查不同的属性(例如Operand用于一元表达式,Arguments用于函数调用),以确定这些属性中的任何一个都是TSource的属性。然后我发现了表达式类型列表,我放弃了——即使我只支持最常见的类型,它仍然有很多工作要做。然后我找到了ExpressionVisitor。它看起来更好,但它仍然有很多工作来覆盖访问者的方法,我想知道是否有其他的选择,也许使用一个更专业的框架,在我投入我的时间之前。

遍历表达式树并提取参数

我认为正如你所说,使用ExpressionVisitor是一个很好的方法。您不需要实现所有Visit...方法,因为它们已经有默认实现。根据我的理解,你想要的是在lambda函数

中找到特定类型的所有属性访问
public class MemberAccessVisitor : ExpressionVisitor
{
    private readonly Type declaringType;
    private IList<string> propertyNames = new List<string>();
    public MemberAccessVisitor(Type declaringType)
    {
        this.declaringType = declaringType;
    }
    public IEnumerable<string> PropertyNames { get { return propertyNames; } }
    public override Expression Visit(Expression expr)
    {
        if (expr != null && expr.NodeType == ExpressionType.MemberAccess)
        {
            var memberExpr = (MemberExpression)expr;
            if (memberExpr.Member.DeclaringType == declaringType)
            {
                propertyNames.Add(memberExpr.Member.Name);
            }
        }
        return base.Visit(expr);
    }
}

这可以进一步改进为您想要的,通过检查成员是一个属性,也得到PropertyInfo而不是字符串

可以这样使用:

var visitor = new MemberAccessVisitor(typeof(TSource));
visitor.Visit(memberMap);
var propertyNames = visitor.PropertyNames;