当通过lambda表达式传入时,是否有更好的方法来获得属性名?
这是我目前拥有的。
eg.
GetSortingInfo<User>(u => u.UserId);
它只在属性为字符串时才将其转换为成员表达式。因为不是所有的属性都是字符串,我必须使用object,但它会为那些返回一个unaryexpression。
public static RouteValueDictionary GetInfo<T>(this HtmlHelper html,
Expression<Func<T, object>> action) where T : class
{
var expression = GetMemberInfo(action);
string name = expression.Member.Name;
return GetInfo(html, name);
}
private static MemberExpression GetMemberInfo(Expression method)
{
LambdaExpression lambda = method as LambdaExpression;
if (lambda == null)
throw new ArgumentNullException("method");
MemberExpression memberExpr = null;
if (lambda.Body.NodeType == ExpressionType.Convert)
{
memberExpr =
((UnaryExpression)lambda.Body).Operand as MemberExpression;
}
else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpr = lambda.Body as MemberExpression;
}
if (memberExpr == null)
throw new ArgumentException("method");
return memberExpr;
}
使用c# 7模式匹配:
public static string GetMemberName<T>(this Expression<T> expression)
{
switch (expression.Body)
{
case MemberExpression m:
return m.Member.Name;
case UnaryExpression u when u.Operand is MemberExpression m:
return m.Member.Name;
default:
throw new NotImplementedException(expression.GetType().ToString());
}
}
例子:
public static RouteValueDictionary GetInfo<T>(this HtmlHelper html,
Expression<Func<T, object>> action) where T : class
{
var name = action.GetMemberName();
return GetInfo(html, name);
}
[更新]c# 8模式匹配:
public static string GetMemberName<T>(this Expression<T> expression) => expression.Body switch
{
MemberExpression m => m.Member.Name,
UnaryExpression u when u.Operand is MemberExpression m => m.Member.Name,
_ => throw new NotImplementedException(expression.GetType().ToString())
};
static void Main(string[] args)
{
var prop = GetPropertyInfo<MyDto>(_ => _.MyProperty);
MyDto dto = new MyDto();
dto.MyProperty = 666;
var value = prop.GetValue(dto);
// value == 666
}
class MyDto
{
public int MyProperty { get; set; }
}
public static PropertyInfo GetPropertyInfo<TSource>(Expression<Func<TSource, object>> propertyLambda)
{
Type type = typeof(TSource);
var member = propertyLambda.Body as MemberExpression;
if (member == null)
{
var unary = propertyLambda.Body as UnaryExpression;
if (unary != null)
{
member = unary.Operand as MemberExpression;
}
}
if (member == null)
{
throw new ArgumentException(string.Format("Expression '{0}' refers to a method, not a property.",
propertyLambda.ToString()));
}
var propInfo = member.Member as PropertyInfo;
if (propInfo == null)
{
throw new ArgumentException(string.Format("Expression '{0}' refers to a field, not a property.",
propertyLambda.ToString()));
}
if (type != propInfo.ReflectedType && !type.IsSubclassOf(propInfo.ReflectedType))
{
throw new ArgumentException(string.Format("Expression '{0}' refers to a property that is not from type {1}.",
propertyLambda.ToString(), type));
}
return propInfo;
}
这是一个通用的实现,用于获取struct/class/interface/delegate/array的字段/属性/索引器/方法/扩展方法/委托的字符串名称。我已经测试了静态/实例和非泛型/泛型变体的组合。
//involves recursion
public static string GetMemberName(this LambdaExpression memberSelector)
{
Func<Expression, string> 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);
}
这个东西也可以写在一个简单的while循环中:
//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");
}
}
}
我喜欢递归方法,尽管第二种方法可能更容易阅读。我们可以这样称呼它:
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();
打印最后一个成员。
注意:
对于像a.b.c.这样的链式表达式,将返回“C”。
这并不适用于const,数组索引器或枚举(不可能涵盖所有情况)。
这是卡梅隆提出的方法的更新。第一个参数不是必需的。
public PropertyInfo GetPropertyInfo<TSource, TProperty>(
Expression<Func<TSource, TProperty>> propertyLambda)
{
Type type = typeof(TSource);
MemberExpression member = propertyLambda.Body as MemberExpression;
if (member == null)
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a method, not a property.",
propertyLambda.ToString()));
PropertyInfo propInfo = member.Member as PropertyInfo;
if (propInfo == null)
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a field, not a property.",
propertyLambda.ToString()));
if (type != propInfo.ReflectedType &&
!type.IsSubclassOf(propInfo.ReflectedType))
throw new ArgumentException(string.Format(
"Expresion '{0}' refers to a property that is not from type {1}.",
propertyLambda.ToString(),
type));
return propInfo;
}
您可以执行以下操作:
var propertyInfo = GetPropertyInfo<SomeType>(u => u.UserID);
var propertyInfo = GetPropertyInfo((SomeType u) => u.UserID);
扩展方法:
public static PropertyInfo GetPropertyInfo<TSource, TProperty>(this TSource source,
Expression<Func<TSource, TProperty>> propertyLambda) where TSource : class
{
return GetPropertyInfo(propertyLambda);
}
public static string NameOfProperty<TSource, TProperty>(this TSource source,
Expression<Func<TSource, TProperty>> propertyLambda) where TSource : class
{
PropertyInfo prodInfo = GetPropertyInfo(propertyLambda);
return prodInfo.Name;
}
您可以:
SomeType someInstance = null;
string propName = someInstance.NameOfProperty(i => i.Length);
PropertyInfo propInfo = someInstance.GetPropertyInfo(i => i.Length);
假设(TModel作为类)
Expression<Func<TModel, TValue>> expression
检索属性的名称
expression.GetPropertyInfo().Name;
扩展函数:
public static PropertyInfo GetPropertyInfo<TType, TReturn>(this Expression<Func<TType, TReturn>> property)
{
LambdaExpression lambda = property;
var memberExpression = lambda.Body is UnaryExpression expression
? (MemberExpression)expression.Operand
: (MemberExpression)lambda.Body;
return (PropertyInfo)memberExpression.Member;
}