如何从 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
我已经为 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