12

例如,我有一堂课:

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

我想调用一个方法来根据Id更新此类中的信息:

update(myId, myPerson => myPerson.Name = "abc");

解释:此方法将从数据库中查询并获取Person给定myId的实体,然后设置Name为“abc”,因此它的工作与我所说的相同:

update(myId, myPerson => myPerson.Address = "my address");

是否可以?如果有怎么办?

4

5 回答 5

26

PropertyInfo就像他在回答中所说的那样,我不会使用Reed Copsey,但仅供参考,您可以使用以下方法提取PropertyInfo表达式的 :

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

对于像这样的复合表达式MyPerson.PersonData.PersonID,您可以获取子表达式,直到它们MemberExpressions不再存在为止。

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.
}
于 2013-06-14T19:53:15.803 回答
6

这是可能的,没有必要使用PropertyInfo.

你会像这样设计你的方法:

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;
}
于 2013-06-14T19:10:47.837 回答
4

这是针对现代语法更新的@DanielMöller 答案的一个版本,带有指定的异常消息和文档。

/// <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} ]");
}
于 2018-04-06T14:48:04.457 回答
3

更清洁恕我直言(@DanielMöller 帖子的另一个变体)

        /// <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} ]");
            }
        }
于 2019-02-09T21:25:58.213 回答
0

使用 C# 9 开关表达式更简洁

public static PropertyInfo GetPropertyInfo<T>(this Expression<Func<T, object>> expression) =>
    expression?.Body switch
    {
        null => throw new ArgumentNullException(nameof(expression)),
        UnaryExpression ue when ue.Operand is MemberExpression me => (PropertyInfo)me.Member,
        MemberExpression me => (PropertyInfo)me.Member,
        _ => throw new ArgumentException($"The expression doesn't indicate a valid property. [ {expression} ]")
    };
于 2021-11-09T02:47:24.143 回答