遍历表达式树并提取参数
Traverse an expression tree and extract parameters
我正在编写一种映射工具。我有一个看起来像这样的方法(简化):
public void RegisterMapping<TTarget, TSource>(string propertyName,
Expression<Func<TSource, object>> memberMap)
memberMap
是定义如何将 属性 从 TSource
转换为 TTarget
的表达式。对于业务逻辑,我需要从中提取对 TSource
属性的所有引用。例如,来自
x => x.Customers.Where(c => c.Orders.Any())
我想得到 Customers
,从
x => x.FirstName + " " + x.LastName
FirstName
和 LastName
(可以是字符串[],PropertyInfo
很容易转换成)。
我该怎么做?我的第一种方法是手动遍历树,检查节点类型并根据节点类型检查不同的属性(例如,Operand
用于一元表达式,Arguments
用于函数调用)以确定其中任何一个是 TSource
的 属性。然后我发现了expression kind list and I gave up -- even if I support only the most common kinds, it's still lots of work. Then I found the 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;
我正在编写一种映射工具。我有一个看起来像这样的方法(简化):
public void RegisterMapping<TTarget, TSource>(string propertyName,
Expression<Func<TSource, object>> memberMap)
memberMap
是定义如何将 属性 从 TSource
转换为 TTarget
的表达式。对于业务逻辑,我需要从中提取对 TSource
属性的所有引用。例如,来自
x => x.Customers.Where(c => c.Orders.Any())
我想得到 Customers
,从
x => x.FirstName + " " + x.LastName
FirstName
和 LastName
(可以是字符串[],PropertyInfo
很容易转换成)。
我该怎么做?我的第一种方法是手动遍历树,检查节点类型并根据节点类型检查不同的属性(例如,Operand
用于一元表达式,Arguments
用于函数调用)以确定其中任何一个是 TSource
的 属性。然后我发现了expression kind list and I gave up -- even if I support only the most common kinds, it's still lots of work. Then I found the 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;