Retrieving Property name from lambda expression

前端 未结 21 2036
迷失自我
迷失自我 2020-11-21 11:12

Is there a better way to get the Property name when passed in via a lambda expression? Here is what i currently have.

eg.

GetSortingInfo         


        
21条回答
  •  野性不改
    2020-11-21 12:04

    This is a general implementation to get the string name of fields/properties/indexers/methods/extension methods/delegates of struct/class/interface/delegate/array. I have tested with combinations of static/instance and non-generic/generic variants.

    //involves recursion
    public static string GetMemberName(this LambdaExpression memberSelector)
    {
        Func nameSelector = null;  //recursive func
        nameSelector = e => //or move the entire thing to a separate recursive method
        {
            switch (e.NodeType)
            {
                case ExpressionType.Parameter:
                    return ((ParameterExpression)e).Name;
                case ExpressionType.MemberAccess:
                    return ((MemberExpression)e).Member.Name;
                case ExpressionType.Call:
                    return ((MethodCallExpression)e).Method.Name;
                case ExpressionType.Convert:
                case ExpressionType.ConvertChecked:
                    return nameSelector(((UnaryExpression)e).Operand);
                case ExpressionType.Invoke:
                    return nameSelector(((InvocationExpression)e).Expression);
                case ExpressionType.ArrayLength:
                    return "Length";
                default:
                    throw new Exception("not a proper member selector");
            }
        };
    
        return nameSelector(memberSelector.Body);
    }
    

    This thing can be written in a simple while loop too:

    //iteration based
    public static string GetMemberName(this LambdaExpression memberSelector)
    {
        var currentExpression = memberSelector.Body;
    
        while (true)
        {
            switch (currentExpression.NodeType)
            {
                case ExpressionType.Parameter:
                    return ((ParameterExpression)currentExpression).Name;
                case ExpressionType.MemberAccess:
                    return ((MemberExpression)currentExpression).Member.Name;
                case ExpressionType.Call:
                    return ((MethodCallExpression)currentExpression).Method.Name;
                case ExpressionType.Convert:
                case ExpressionType.ConvertChecked:
                    currentExpression = ((UnaryExpression)currentExpression).Operand;
                    break;
                case ExpressionType.Invoke:
                    currentExpression = ((InvocationExpression)currentExpression).Expression;
                    break;
                case ExpressionType.ArrayLength:
                    return "Length";
                default:
                    throw new Exception("not a proper member selector");
            }
        }
    }
    

    I like the recursive approach, though the second one might be easier to read. One can call it like:

    someExpr = x => x.Property.ExtensionMethod()[0]; //or
    someExpr = x => Static.Method().Field; //or
    someExpr = x => VoidMethod(); //or
    someExpr = () => localVariable; //or
    someExpr = x => x; //or
    someExpr = x => (Type)x; //or
    someExpr = () => Array[0].Delegate(null); //etc
    
    string name = someExpr.GetMemberName();
    

    to print the last member.

    Note:

    1. In case of chained expressions like A.B.C, "C" is returned.

    2. This doesn't work with consts, array indexers or enums (impossible to cover all cases).

提交回复
热议问题