按属性名称(字符串值)排序列表? [复制]

Posted

技术标签:

【中文标题】按属性名称(字符串值)排序列表? [复制]【英文标题】:Order a list by a property name(string value)? [duplicate] 【发布时间】:2014-10-30 06:15:43 【问题描述】:

我有一个objects 的列表。如何使用属性名称订购此列表?

string orderbyField = "Code";
List<object> l = FillList();
l = l.OrderBy(o => orderbyField);

我可以为这个问题提供扩展程序吗?

【问题讨论】:

能否提供你FillList的完整代码sn-p 关注的是你有 object 的列表而不是特定的类,所以你如何确保列表中的所有对象都具有名为“代码”的属性.. 更好创建特定类型的列表..或具有多种类型使用接口类型列表 我没有特定的类型。 FillList 返回一个 LINQ 实体对象。 s.t 像 dc.Employer。我将此列表用作 MVC.NET 中自定义网格的数据源。 如果您没有特定类型,这意味着您无法确保该对象具有 Code 属性。那么创建此 OrderBy 没有意义b> LINQ 但我在运行时(动态)知道属性名称。例如,单击网格上的代码标题,我从对象中获取“代码”属性。如果有办法做出这样的事情:list.OrderBy(o => o.GetType().GetProperty("Code"); 【参考方案1】:

如果您不必以字符串形式提供属性名称,使用dynamic 非常简单:

List<object> l = FillList();
l = l.OrderBy(o => ((dynamic)o).Id);

如果属性名称必须是一个字符串,那么它会变得有点复杂,但可以使用反射来完成(虽然它不是很有效):

l = l.OrderBy(o => o.GetType()
                    .GetProperty("Code")
                    .GetValue(o, null));

您还应该考虑添加一些错误处理,例如如果该属性不存在。

此外,如果列表中的所有元素都具有相同的 runtime 类型,那么使用表达式树编译 getter 函数并重用它(而不是直接使用反射)会更有效。

public static Func<object, object> CreateGetter(Type runtimeType, string propertyName)

    var propertyInfo = runtimeType.GetProperty(propertyName);

    // create a parameter (object obj)
    var obj = Expression.Parameter(typeof(object), "obj");  

    // cast obj to runtimeType
    var objT = Expression.TypeAs(obj, runtimeType); 

    // property accessor
    var property = Expression.Property(objT, propertyInfo); 

    var convert = Expression.TypeAs(property, typeof(object));
    return (Func<object, object>)Expression.Lambda(convert, obj).Compile();

并像这样使用它:

var codeGetter = CreateGetter(l[0].GetType(), "Code"); // using the 1st element as an example
l = l.OrderBy(o => codeGetter(o));

【讨论】:

【参考方案2】:

按属性名排序,没有类型反射

    public static class IQueryableExtensions
    
      public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string 
      propertyName)
      
      return (IQueryable<T>)OrderBy((IQueryable)source, propertyName);
      

      public static IQueryable OrderBy(this IQueryable source, string propertyName)
      
        var x = Expression.Parameter(source.ElementType, "x");
        var body = propertyName.Split('.').Aggregate<string, Expression>(x, 
        Expression.PropertyOrField);

        var selector = Expression.Lambda
         (Expression.PropertyOrField(x, propertyName), x);

         return source.Provider.CreateQuery(
            Expression.Call(typeof(Queryable), "OrderBy", new Type[]  
             source.ElementType, selector.Body.Type ,
                 source.Expression, selector
                 ));
      

      public static IQueryable<T> OrderByDescending<T>(this IQueryable<T> source, 
      string propertyName)
      
        return (IQueryable<T>)OrderByDescending((IQueryable)source, propertyName);
      

      public static IQueryable OrderByDescending(this IQueryable source, string 
      propertyName)
      
        var x = Expression.Parameter(source.ElementType, "x");
        var selector = Expression.Lambda(Expression.PropertyOrField(x, 
        propertyName),x);
        return source.Provider.CreateQuery(
            Expression.Call(typeof(Queryable), "OrderByDescending", new Type[]  
             source.ElementType, selector.Body.Type ,
                 source.Expression, selector
                 ));
      

【讨论】:

相当不错。很不错。【参考方案3】:
    public static IEnumerable<TSource> ComplexOrderBy<TSource>(this IEnumerable<TSource> source, string orderString)
    
        if (string.IsNullOrWhiteSpace(orderString))
        
            return source;
        
        IOrderedEnumerable<TSource> orderedQuery = null;
        var sortingFields = orderString.Split(new[]  "," , StringSplitOptions.RemoveEmptyEntries);
        var propertyNames = typeof(TSource).GetProperties().Select(prop => prop.Name.ToLower()).ToImmutableHashSet();
        for (var i = 0; i < sortingFields.Length; i++)
        
            var sortingSet = sortingFields[i].Split(new[]  " " , StringSplitOptions.RemoveEmptyEntries);
            var sortBy = sortingSet[0].ToLower();
            var isDescending = sortingSet.Length > 1 && sortingSet[1].Trim().ToLower() == "desc";
            try
            
                var propertySelector = sortBy.GetPropertySelector<TSource>();
                orderedQuery = isDescending
                                ?
                                (i == 0 ? source.OrderByDescending(propertySelector.Compile()) : orderedQuery.OrderByDescending(propertySelector.Compile()).ThenByDescending(propertySelector.Compile()))
                                :
                                (i == 0 ? source.OrderBy(propertySelector.Compile()) : orderedQuery.ThenBy(propertySelector.Compile()));
            
            // Just ignoring illegal properties for simplicity
            catch (ArgumentNullException)  
            catch (ArgumentException)  
        
        return orderedQuery ?? source;
    

    public static Expression<Func<T, object>> GetPropertySelector<T>(this string propertyName)
    
        var parameterExpression = Expression.Parameter(typeof(T), "x");
        Expression body = parameterExpression;
        foreach (var member in propertyName.Split('.'))
        
            body = Expression.Property(body, member);
        
        return Expression.Lambda<Func<T, object>>(Expression.Convert(body, typeof(object)), parameterExpression);
    

【讨论】:

“GetPropertySelector”对我来说并不熟悉。也是自定义代码? @antal-istván 感谢您指出这一点 :) 我添加了我错过的简短自定义方法

以上是关于按属性名称(字符串值)排序列表? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何按整数属性对类列表进行排序? [复制]

如何按 .NET 2.0 中的特定属性对列表进行排序? [复制]

Magento列表按属性排序按属性排序而不是值排序

按属性值对对象列表进行排序[重复]

根据两个或多个值对通用列表进行排序

按多个字段排序列表(C#)? [复制]