我正在开发一个使用lambda表达式来指定属性的API。我正在使用这个与此类似的着名代码(这是简化和不完整的,只是为了说清楚我在说什么):
public void Foo<T, P>(Expression<Func<T, P>> action)
{
var expression = (MemberExpression)action.Body;
string propertyName = expression.Member.Name;
// ...
}
这样称呼:
Foo((String x) => x.Length);
现在我想通过链接属性名来指定属性路径,如下所示:
Foo((MyClass x) => x.Name.Length);
Foo应该能够将路径拆分为其属性名称("Name"
和"Length"
)。有没有办法通过合理的努力来做到这一点?
有一个somehow similar looking question,但我认为他们正试图在那里组合lambda表达式。
Another question也在处理嵌套的属性名称,但我真的不明白他们在说什么。
答案 0 :(得分:28)
这样的东西?
public void Foo<T, P>(Expression<Func<T, P>> expr)
{
MemberExpression me;
switch (expr.Body.NodeType)
{
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
var ue = expr.Body as UnaryExpression;
me = ((ue != null) ? ue.Operand : null) as MemberExpression;
break;
default:
me = expr.Body as MemberExpression;
break;
}
while (me != null)
{
string propertyName = me.Member.Name;
Type propertyType = me.Type;
Console.WriteLine(propertyName + ": " + propertyType);
me = me.Expression as MemberExpression;
}
}
答案 1 :(得分:14)
我和ExpressionVisitor玩了一会儿:
public static class PropertyPath<TSource>
{
public static IReadOnlyList<MemberInfo> Get<TResult>(Expression<Func<TSource, TResult>> expression)
{
var visitor = new PropertyVisitor();
visitor.Visit(expression.Body);
visitor.Path.Reverse();
return visitor.Path;
}
private class PropertyVisitor : ExpressionVisitor
{
internal readonly List<MemberInfo> Path = new List<MemberInfo>();
protected override Expression VisitMember(MemberExpression node)
{
if (!(node.Member is PropertyInfo))
{
throw new ArgumentException("The path can only contain properties", nameof(node));
}
this.Path.Add(node.Member);
return base.VisitMember(node);
}
}
}
用法:
var path = string.Join(".", PropertyPath<string>.Get(x => x.Length).Select(p => p.Name));
答案 2 :(得分:11)
老问题,我知道......但如果它只是你需要的名字,那么更简单的方法就是:
expr.ToString().Split('.').Skip(1)
修改强>
public class A
{
public B Property { get; set; }
}
public class B
{
public C field;
}
[Fact]
public void FactMethodName()
{
var exp = (Expression<Func<A, object>>) (x => x.Property.field);
foreach (var part in exp.ToString().Split('.').Skip(1))
Console.WriteLine(part);
// Output:
// Property
// field
}
答案 3 :(得分:0)
我在客户端和服务器之间具有共享的.NET标准DTO,表达式是创建可在Api端重建和执行的查询字符串的好方法。
创建在线安全类型查询的理想方法。
我离题了,我还需要一个属性路径
x => x.Siblings.Age
产生类似这样的字符串
"Siblings.Age"
我同意了
public static string GetMemberPath(MemberExpression me)
{
var parts = new List<string>();
while (me != null)
{
parts.Add(me.Member.Name);
me = me.Expression as MemberExpression;
}
parts.Reverse();
return string.Join(".", parts);
}
答案 4 :(得分:0)
public static string GetPath<T, TProperty>(this Expression<Func<T, TProperty>> exp)
{
return string.Join(".", GetItemsInPath(exp).Reverse());
}
private static IEnumerable<string> GetItemsInPath<T, TProperty>(Expression<Func<T, TProperty>> exp)
{
if (exp == null)
{
yield break;
}
var memberExp = FindMemberExpression(exp.Body);
while (memberExp != null)
{
yield return memberExp.Member.Name;
memberExp = FindMemberExpression(memberExp.Expression);
}
}