如何从 C# 中的表达式中提取所有 属性-access 语句?

How to extract all property-access statements from an expression in C#?

我已经为 C#/WPF 实现了一个命令条件机制,它可以自动执行驱动,无论是否可以执行特定操作。只是为了说明问题的动机,这里有一个简单的例子。

this.DocumentExistsCondition = new Condition(false);
this.SaveDocumentCommand = new AppCommand(() => DoSaveDocument, DocumentExistsCondition);

然后我可以像这样改变条件:

this.DocumentExistsCondition.Value = false;

以便所有相关命令更改其可用性。该机制很复杂,允许聚合条件、对它们进行与-ing 和或-ing 等。

其中一种条件类型允许用户指定成员中的成员并观察其值,例如:

documentExistsCondition = new MutablePropertyNotNullCondition<DocumentsManager, BaseDocumentViewModel>(documentsManager, dm => dm.ActiveDocument);

这进一步简化了事情,因为我不再需要主动跟踪 属性,这个条件实现为我完成了。

虽然我的机制非常有限,因为指定为第二个参数的表达式 必须 是 属性-access-statement。那是因为我正在提取 属性 名称,检查源实例是否实现 INotifyPropertyChange 然后挂钩这个特定的 属性 更改。

我想扩展此机制并允许用户提供任何对某个对象求值的表达式,但使用来自源的单个 属性,即

documentExistsCondition = new MutablePropertyNotNullCondition<DocumentsManager, BaseDocumentViewModel>(documentsManager, dm => dm.ActiveDocument != null ? dm.ActiveDocument : BaseDocumentViewModel.Default);

我目前提取 属性 的方式如下:

private PropertyInfo GetPropertyInfo<TClass, TClassProperty>(Expression<Func<TClass, TClassProperty>> propertyLambda)
{
    Type type = typeof(TClass);

    var member = propertyLambda.Body as MemberExpression;
    if (member == null)
        throw new ArgumentException(string.Format(
            "Expression '{0}' refers to a method, not a property.",
            propertyLambda.ToString()));

    var propInfo = member.Member as PropertyInfo;
    if (propInfo == null)
        throw new ArgumentException(string.Format(
            "Expression '{0}' refers to a field, not a property.",
            propertyLambda.ToString()));

    if (type != propInfo.ReflectedType &&
        !type.IsSubclassOf(propInfo.ReflectedType))
        throw new ArgumentException(string.Format(
            "Expression '{0}' refers to a property that is not from type {1}.",
            propertyLambda.ToString(),
            type));

    return propInfo;
}

有没有一种方法可以简单地从复杂语句中提取所有 属性-access 语句的列表?还是我必须递归地遍历各种表达式并手动完成?

换句话说:我可以在某个通用级别(如 foreach var childStatement in ChildStatements) { ... })遍历语句树吗?

您应该可以为此使用 ExpressionVisitor

public class MemberExpressionRecorder : ExpressionVisitor
{
    public List<MemberExpression> MemberExpressions { get; } = new List<MemberExpression>();

    protected override Expression VisitMember(MemberExpression node)
    {
        MemberExpressions.Add(node);
        return base.VisitMember(node);
    }
}

...

var visitor = new MemberExpressionRecorder();
visitor.Visit(someExpression);
// access visitor.MemberExpressions