2009-11-03 3 views
29

람다 식을 사용하여 속성을 지정하는 API를 개발하고 있습니다.C# : 람다 식 체인의 속성 이름 얻기

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); 

푸 그 속성 이름으로 경로를 분할 할 수 있어야한다 ("Name" 및,917을 :

는 지금은이 같은 속성 이름을 체인에 의해 속성 경로를 지정하고 싶습니다). 합리적인 노력으로이를 수행 할 수있는 방법이 있습니까?


거기에 somehow similar looking question가 있지만 나는 거기에 람다 식을 결합하려고합니다.

Another question도 중첩 된 속성 이름을 처리하지만 실제로 어떤 내용인지는 이해할 수 없습니다.

답변

27

이와 비슷한?

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

와우,이 작품, 그것은 매우 간단합니다. 고마워요! –

+1

@StefanSteinegger @StefanSteinegger 이전 질문, 필자도 알아 두어야 할 것은 'expr.ToString(). Split ('. '). Skip (1)'이 더 간단 할 것입니다. :) – asgerhallas

+2

@asgerhallas : you 다른 대답을 추가 할 수 있습니다. –

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 
} 
+0

흠, 그게 저에게 효과적이지 않았습니다 ('.ToString'은 마지막 속성 이름 만 부여했습니다). 사용법이 더 큰 코드 샘플을 가지고 있습니까? – Pat

+0

@Pat 일부 작업 코드에서 편집했습니다. 희망이 도움이됩니다. 조금 늦었지 만 :) – asgerhallas

+0

ToString은 가치 형이 박스 처리되는 경우에는 작동하지 않을 것입니다. 조심하십시오. – nawfal

11

조금 놀았습니다 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)); 
+0

ExpressionVisitor에 감사드립니다. 이것은 정말 깨끗한 해결책이었습니다. 개인적으로 잠금을 사용하는 대신 메서드 호출 당 PathVisitor 인스턴스를 만들지 만 문서는 권장 사항에 대해 아무 말도하지 않거나 만들려는 무거운 개체 인 경우에는 아무 것도 말하지 않습니다. Dispose()가 없으므로 많은 리소스가 필요하지 않습니다. – angularsen

+0

잠금없이 수정 된 버전을 추가하고 일반 매개 변수를 클래스로 이동하여 구성 가능한 문자열 구분 기호가있는 문자열을 반환하는 TSource 및 편리한 메서드 만 지정하면됩니다. https://gist.github.com/anjdreas/862c1cd9983d7525d2ddee0bb2706c3a – angularsen

+0

예 , 잠그고 거기서 바보가되어 대답을 업데이트합니다. –