我正在写一种绘图工具.我有一个看起来像这样的方法(简化):
public void RegisterMapping<TTarget, TSource>(string propertyName,
Expression<Func<TSource, object>> memberMap)
memberMap是一个表达式,用于定义如何将属性从TSource转换为TTarget.对于业务逻辑,我需要从中提取对TSource属性的所有引用.例如,来自
x => x.Customers.Where(c => c.Orders.Any())
我想得到客户,来自
x => x.FirstName + " " + x.LastName
FirstName和LastName(可以是string [],PropertyInfo很容易转换为).
我该怎么做?我的第一种方法是手动遍历树,检查节点类型并根据节点类型检查不同的属性(例如,一元表达式的操作数,函数调用的参数)以确定其中任何一个是TSource的属性.然后我发现了expression kind list并且我放弃了 – 即使我只支持最常见的类型,它仍然很多工作.然后我找到了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;