当通过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;
}
我发现了另一种方法,就是让源和属性具有强类型,并显式地推断lambda的输入。不确定这是否是正确的术语,但这是结果。
public static RouteValueDictionary GetInfo<T,P>(this HtmlHelper html, Expression<Func<T, P>> action) where T : class
{
var expression = (MemberExpression)action.Body;
string name = expression.Member.Name;
return GetInfo(html, name);
}
然后像这样叫它。
GetInfo((User u) => u.UserId);
瞧,它起作用了。
我正在使用一个扩展方法的前c# 6项目和名称()的目标c# 6。
public static class MiscExtentions
{
public static string NameOf<TModel, TProperty>(this object @object, Expression<Func<TModel, TProperty>> propertyExpression)
{
var expression = propertyExpression.Body as MemberExpression;
if (expression == null)
{
throw new ArgumentException("Expression is not a property.");
}
return expression.Member.Name;
}
}
我称之为:
public class MyClass
{
public int Property1 { get; set; }
public string Property2 { get; set; }
public int[] Property3 { get; set; }
public Subclass Property4 { get; set; }
public Subclass[] Property5 { get; set; }
}
public class Subclass
{
public int PropertyA { get; set; }
public string PropertyB { get; set; }
}
// result is Property1
this.NameOf((MyClass o) => o.Property1);
// result is Property2
this.NameOf((MyClass o) => o.Property2);
// result is Property3
this.NameOf((MyClass o) => o.Property3);
// result is Property4
this.NameOf((MyClass o) => o.Property4);
// result is PropertyB
this.NameOf((MyClass o) => o.Property4.PropertyB);
// result is Property5
this.NameOf((MyClass o) => o.Property5);
它可以很好地处理字段和属性。
下面是基于这个答案获取PropertyInfo的另一种方法。它消除了对对象实例的需要。
/// <summary>
/// Get metadata of property referenced by expression. Type constrained.
/// </summary>
public static PropertyInfo GetPropertyInfo<TSource, TProperty>(Expression<Func<TSource, TProperty>> propertyLambda)
{
return GetPropertyInfo((LambdaExpression) propertyLambda);
}
/// <summary>
/// Get metadata of property referenced by expression.
/// </summary>
public static PropertyInfo GetPropertyInfo(LambdaExpression propertyLambda)
{
// https://stackoverflow.com/questions/671968/retrieving-property-name-from-lambda-expression
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(propertyLambda.Parameters.Count() == 0)
throw new ArgumentException(String.Format(
"Expression '{0}' does not have any parameters. A property expression needs to have at least 1 parameter.",
propertyLambda.ToString()));
var type = propertyLambda.Parameters[0].Type;
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;
}
它可以这样调用:
var propertyInfo = GetPropertyInfo((User u) => u.UserID);
当涉及到Array.Length时,有一个边缘情况。虽然'Length'被公开为属性,但您不能在任何前面提出的解决方案中使用它。
using Contract = System.Diagnostics.Contracts.Contract;
using Exprs = System.Linq.Expressions;
static string PropertyNameFromMemberExpr(Exprs.MemberExpression expr)
{
return expr.Member.Name;
}
static string PropertyNameFromUnaryExpr(Exprs.UnaryExpression expr)
{
if (expr.NodeType == Exprs.ExpressionType.ArrayLength)
return "Length";
var mem_expr = expr.Operand as Exprs.MemberExpression;
return PropertyNameFromMemberExpr(mem_expr);
}
static string PropertyNameFromLambdaExpr(Exprs.LambdaExpression expr)
{
if (expr.Body is Exprs.MemberExpression) return PropertyNameFromMemberExpr(expr.Body as Exprs.MemberExpression);
else if (expr.Body is Exprs.UnaryExpression) return PropertyNameFromUnaryExpr(expr.Body as Exprs.UnaryExpression);
throw new NotSupportedException();
}
public static string PropertyNameFromExpr<TProp>(Exprs.Expression<Func<TProp>> expr)
{
Contract.Requires<ArgumentNullException>(expr != null);
Contract.Requires<ArgumentException>(expr.Body is Exprs.MemberExpression || expr.Body is Exprs.UnaryExpression);
return PropertyNameFromLambdaExpr(expr);
}
public static string PropertyNameFromExpr<T, TProp>(Exprs.Expression<Func<T, TProp>> expr)
{
Contract.Requires<ArgumentNullException>(expr != null);
Contract.Requires<ArgumentException>(expr.Body is Exprs.MemberExpression || expr.Body is Exprs.UnaryExpression);
return PropertyNameFromLambdaExpr(expr);
}
现在看用法示例:
int[] someArray = new int[1];
Console.WriteLine(PropertyNameFromExpr( () => someArray.Length ));
如果PropertyNameFromUnaryExpr没有检查ArrayLength, "someArray"将被打印到控制台(编译器似乎生成了对支持Length字段的直接访问,作为优化,甚至在调试中,因此是特殊情况)。