获取作为lambda表达式传递的参数的PropertyInfo
本文关键字:参数 PropertyInfo 表达式 lambda 获取 | 更新日期: 2023-09-27 18:18:40
例如,我有一个类:
public class Person
{
public int Id;
public string Name, Address;
}
,我想调用一个方法来更新这个类的信息,基于Id:
update(myId, myPerson => myPerson.Name = "abc");
explain:该方法将从数据库中查询并获得给定myId的Person
实体,然后将Name
设置为"abc",因此它做的工作与我调用的相同:
update(myId, myPerson => myPerson.Address = "my address");
是可能的吗?如果有,那是怎么回事?
我不会使用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.
}
这是可能的,并且不需要使用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;
}
以下是针对现代语法更新的@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} ]");
}
更干净的IMHO (@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} ]");
}
}
使用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} ]")
};