Get PropertyInfo of a parameter passed as lambda expression

前端 未结 4 1793
攒了一身酷
攒了一身酷 2021-01-11 14:49

For example, I have a class:

public class Person
{
  public int Id;
  public string Name, Address;
}

and I want to call a method to update

相关标签:
4条回答
  • 2021-01-11 15:13

    Here is a version of @DanielMöller's answer updated for modern syntax, with specified exception messages, and documentation.

    /// <summary>
    ///     Gets the corresponding <see cref="PropertyInfo" /> from an <see cref="Expression" />.
    /// </summary>
    /// <param name="property">The expression that selects the property to get info on.</param>
    /// <returns>The property info collected from the expression.</returns>
    /// <exception cref="ArgumentNullException">When <paramref name="property" /> is <c>null</c>.</exception>
    /// <exception cref="ArgumentException">The expression doesn't indicate a valid property."</exception>
    private PropertyInfo GetPropertyInfo<T, P>(Expression<Func<T, P>> property)
    {
        if (property == null) {
            throw new ArgumentNullException(nameof(property));
        }
    
        if (property.Body is UnaryExpression unaryExp) {
            if (unaryExp.Operand is MemberExpression memberExp) {
                return (PropertyInfo)memberExp.Member;
            }
        }
        else if (property.Body is MemberExpression memberExp) {
            return (PropertyInfo)memberExp.Member;
        }
    
        throw new ArgumentException($"The expression doesn't indicate a valid property. [ {property} ]");
    }
    
    0 讨论(0)
  • 2021-01-11 15:27

    I wouldn't use PropertyInfo, just like Reed Copsey said in his answer, but just for information, you can extract the PropertyInfo of an expression with this:

    public PropertyInfo GetPropertyFromExpression<T>(Expression<Func<T, object>> GetPropertyLambda)
    {
        MemberExpression Exp = null;
    
        //this line is necessary, because sometimes the expression comes in as Convert(originalexpression)
        if (GetPropertyLambda.Body is UnaryExpression)
        {
            var UnExp = (UnaryExpression)GetPropertyLambda.Body;
            if (UnExp.Operand is MemberExpression)
            {
                Exp = (MemberExpression)UnExp.Operand;
            }
            else
                throw new ArgumentException();
        }
        else if (GetPropertyLambda.Body is MemberExpression)
        {
            Exp = (MemberExpression)GetPropertyLambda.Body;
        }
        else
        {
            throw new ArgumentException();
        }
    
        return (PropertyInfo)Exp.Member;
    }
    

    In the case of a composite expression like MyPerson.PersonData.PersonID, you could go getting the sub expressions until they're not MemberExpressions anymore.

    public PropertyInfo GetPropertyFromExpression<T>(Expression<Func<T, object>> GetPropertyLambda)
    {
        //same body of above method without the return line.
        //....
        //....
        //....
    
        var Result = (PropertyInfo)Exp.Member;
    
        var Sub = Exp.Expression;
    
        while (Sub is MemberExpression)
        {
            Exp = (MemberExpression)Sub;
            Result = (PropertyInfo)Exp.Member;
            Sub = Exp.Expression;
        }
    
        return Result;
        //beware, this will return the last property in the expression.
        //when using GetValue and SetValue, the object needed will not be
        //the first object in the expression, but the one prior to the last.
        //To use those methods with the first object, you will need to keep
        //track of all properties in all member expressions above and do
        //some recursive Get/Set following the sequence of the expression.
    }
    
    0 讨论(0)
  • 2021-01-11 15:32

    This is possible, and there's no need to use PropertyInfo.

    You'd design your method like so:

    public bool Update<T>(int id, Action<T> updateMethod)
        // where T  : SomeDbEntityType
    {
        T entity = LoadFromDatabase(id); // Load your "person" or whatever
    
        if (entity == null) 
            return false; // If you want to support fails this way, etc...
    
        // Calls the method on the person
        updateMethod(entity);
    
        SaveEntity(entity); // Do whatever you need to persist the values
    
        return true;
    }
    
    0 讨论(0)
  • 2021-01-11 15:37

    Even cleaner IMHO (another variant on @DanielMöller's post)

            /// <summary>
            ///     Gets the corresponding <see cref="PropertyInfo" /> from an <see cref="Expression" />.
            /// </summary>
            /// <param name="expression">The expression that selects the property to get info on.</param>
            /// <returns>The property info collected from the expression.</returns>
            /// <exception cref="ArgumentNullException">When <paramref name="expression" /> is <c>null</c>.</exception>
            /// <exception cref="ArgumentException">The expression doesn't indicate a valid property."</exception>
            public static PropertyInfo GetPropertyInfo<T>(this Expression<Func<T, object>> expression)
            {
                switch (expression?.Body) {
                    case null:
                        throw new ArgumentNullException(nameof(expression));
                    case UnaryExpression unaryExp when unaryExp.Operand is MemberExpression memberExp:
                        return (PropertyInfo)memberExp.Member;
                    case MemberExpression memberExp:
                        return (PropertyInfo)memberExp.Member;
                    default:
                        throw new ArgumentException($"The expression doesn't indicate a valid property. [ {expression} ]");
                }
            }
    
    0 讨论(0)
提交回复
热议问题