当通过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;
}

当前回答

如果你想要获得多个字段,我就保留这个函数:

/// <summary>
    /// Get properties separated by , (Ex: to invoke 'd => new { d.FirstName, d.LastName }')
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="exp"></param>
    /// <returns></returns>
    public static string GetFields<T>(Expression<Func<T, object>> exp)
    {
        MemberExpression body = exp.Body as MemberExpression;
        var fields = new List<string>();
        if (body == null)
        {
            NewExpression ubody = exp.Body as NewExpression;
            if (ubody != null)
                foreach (var arg in ubody.Arguments)
                {
                    fields.Add((arg as MemberExpression).Member.Name);
                }
        }

        return string.Join(",", fields);
    }

其他回答

这是另一个答案:

public static string GetPropertyName<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
                                                                      Expression<Func<TModel, TProperty>> expression)
    {
        var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);

        return metaData.PropertyName;
    }

我已经更新了@Cameron的回答,包括一些针对转换类型lambda表达式的安全检查:

PropertyInfo GetPropertyName<TSource, TProperty>(
Expression<Func<TSource, TProperty>> propertyLambda)
{
  var body = propertyLambda.Body;
  if (!(body is MemberExpression member)
    && !(body is UnaryExpression unary
      && (member = unary.Operand as MemberExpression) != null))
    throw new ArgumentException($"Expression '{propertyLambda}' " +
      "does not refer to a property.");

  if (!(member.Member is PropertyInfo propInfo))
    throw new ArgumentException($"Expression '{propertyLambda}' " +
      "refers to a field, not a property.");

  var type = typeof(TSource);
  if (!propInfo.DeclaringType.GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()))
    throw new ArgumentException($"Expresion '{propertyLambda}' " + 
      "refers to a property that is not from type '{type}'.");

  return propInfo;
}

我已经完成了类似于下面方法的INotifyPropertyChanged实现。在这里,属性存储在基类中的字典中,如下所示。当然,使用继承并不总是可取的,但是对于视图模型,我认为它是可以接受的,并且在视图模型类中给出了非常干净的属性引用。

public class PhotoDetailsViewModel
    : PropertyChangedNotifierBase<PhotoDetailsViewModel>
{
    public bool IsLoading
    {
        get { return GetValue(x => x.IsLoading); }
        set { SetPropertyValue(x => x.IsLoading, value); }
    }

    public string PendingOperation
    {
        get { return GetValue(x => x.PendingOperation); }
        set { SetPropertyValue(x => x.PendingOperation, value); }
    }

    public PhotoViewModel Photo
    {
        get { return GetValue(x => x.Photo); }
        set { SetPropertyValue(x => x.Photo, value); }
    }
}

稍微复杂一些的基类如下所示。它处理从lambda表达式到属性名的转换。注意,这些属性实际上是伪属性,因为只使用了名称。但是它对于视图模型和视图模型上的属性引用来说是透明的。

public class PropertyChangedNotifierBase<T> : INotifyPropertyChanged
{
    readonly Dictionary<string, object> _properties = new Dictionary<string, object>();

    protected U GetValue<U>(Expression<Func<T, U>> property)
    {
        var propertyName = GetPropertyName(property);

        return GetValue<U>(propertyName);
    }

    private U GetValue<U>(string propertyName)
    {
        object value;

        if (!_properties.TryGetValue(propertyName, out value))
        {
            return default(U);
        }

        return (U)value;
    }

    protected void SetPropertyValue<U>(Expression<Func<T, U>> property, U value)
    {
        var propertyName = GetPropertyName(property);

        var oldValue = GetValue<U>(propertyName);

        if (Object.ReferenceEquals(oldValue, value))
        {
            return;
        }
        _properties[propertyName] = value;

        RaisePropertyChangedEvent(propertyName);
    }

    protected void RaisePropertyChangedEvent<U>(Expression<Func<T, U>> property)
    {
        var name = GetPropertyName(property);
        RaisePropertyChangedEvent(name);
    }

    protected void RaisePropertyChangedEvent(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    private static string GetPropertyName<U>(Expression<Func<T, U>> property)
    {
        if (property == null)
        {
            throw new NullReferenceException("property");
        }

        var lambda = property as LambdaExpression;

        var memberAssignment = (MemberExpression) lambda.Body;
        return memberAssignment.Member.Name;
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

现在在c# 6中,你可以简单地使用这样的nameof(User.UserId)

这有很多好处,其中之一是这是在编译时完成的,而不是在运行时。

https://msdn.microsoft.com/en-us/magazine/dn802602.aspx

从。net 4.0开始,你可以使用ExpressionVisitor来查找属性:

class ExprVisitor : ExpressionVisitor {
    public bool IsFound { get; private set; }
    public string MemberName { get; private set; }
    public Type MemberType { get; private set; }
    protected override Expression VisitMember(MemberExpression node) {
        if (!IsFound && node.Member.MemberType == MemberTypes.Property) {
            IsFound = true;
            MemberName = node.Member.Name;
            MemberType = node.Type;
        }
        return base.VisitMember(node);
    }
}

下面是如何使用这个访问者:

var visitor = new ExprVisitor();
visitor.Visit(expr);
if (visitor.IsFound) {
    Console.WriteLine("First property in the expression tree: Name={0}, Type={1}", visitor.MemberName, visitor.MemberType.FullName);
} else {
    Console.WriteLine("No properties found.");
}