Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/267.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用Lambda/Linq对对象排序列表_C#_Linq_Lambda_Linq To Objects - Fatal编程技术网

C# 使用Lambda/Linq对对象排序列表

C# 使用Lambda/Linq对对象排序列表,c#,linq,lambda,linq-to-objects,C#,Linq,Lambda,Linq To Objects,我在字符串中有“按属性排序”的名称。我需要使用Lambda/Linq对对象列表进行排序 例: 公共类员工 { 公共字符串名{set;get;} 公共字符串LastName{set;get;} 公共日期时间DOB{set;get;} } 公共无效排序(参考列表、字符串排序方式、字符串排序方向) { //示例数据: //sortBy=“FirstName” //sortDirection=“ASC”或“DESC” 如果(排序方式==“FirstName”) { list=list.OrderBy(x

我在字符串中有“按属性排序”的名称。我需要使用Lambda/Linq对对象列表进行排序

例:

公共类员工
{
公共字符串名{set;get;}
公共字符串LastName{set;get;}
公共日期时间DOB{set;get;}
}
公共无效排序(参考列表、字符串排序方式、字符串排序方向)
{
//示例数据:
//sortBy=“FirstName”
//sortDirection=“ASC”或“DESC”
如果(排序方式==“FirstName”)
{
list=list.OrderBy(x=>x.FirstName.toList();
}
}
  • 有没有更干净的排序方法来代替使用一堆if来检查字段名(sortBy)
  • sort是否知道数据类型

  • 回答1.:

    您应该能够手动构建一个表达式树,该表达式树可以使用名称作为字符串传递到order中。或者你可以按照另一个答案中的建议使用反射,这可能会减少工作量

    编辑:下面是一个手动构建表达式树的工作示例。(当只知道属性的名称“Value”时,按X.Value排序)。你可以(应该)建立一个通用的方法来做这件事

    using System;
    using System.Linq;
    using System.Linq.Expressions;
    
    class Program
    {
        private static readonly Random rand = new Random();
        static void Main(string[] args)
        {
            var randX = from n in Enumerable.Range(0, 100)
                        select new X { Value = rand.Next(1000) };
    
            ParameterExpression pe = Expression.Parameter(typeof(X), "value");
            var expression = Expression.Property(pe, "Value");
            var exp = Expression.Lambda<Func<X, int>>(expression, pe).Compile();
    
            foreach (var n in randX.OrderBy(exp))
                Console.WriteLine(n.Value);
        }
    
        public class X
        {
            public int Value { get; set; }
        }
    }
    
    使用系统;
    使用System.Linq;
    使用System.Linq.Expressions;
    班级计划
    {
    private static readonly rand=new Random();
    静态void Main(字符串[]参数)
    {
    var randX=可枚举范围(0,100)中的n
    选择新的X{Value=rand.Next(1000)};
    ParameterExpression pe=Expression.Parameter(typeof(X),“value”);
    var expression=expression.Property(pe,“值”);
    var exp=Expression.Lambda(Expression,pe).Compile();
    foreach(randX.OrderBy中的变量n(exp))
    控制台写入线(n值);
    }
    公共X类
    {
    公共int值{get;set;}
    }
    }
    
    但是,构建表达式树需要知道参与类型。在您的使用场景中,这可能是问题,也可能不是问题。如果您不知道应该按什么类型排序,那么使用反射可能会更容易

    回答2.:


    是的,因为如果您没有明确定义比较器,那么Comparer.Default将用于比较。

    您可以使用反射来获取属性的值

    list = list.OrderBy( x => TypeHelper.GetPropertyValue( x, sortBy ) )
               .ToList();
    
    public List<Employee> Sort(List<Employee> list, String sortBy, String sortDirection)
    {
       PropertyInfo property = list.GetType().GetGenericArguments()[0].
                                    GetType().GetProperty(sortBy);
    
       if (sortDirection == "ASC")
       {
          return list.OrderBy(e => property.GetValue(e, null));
       }
       if (sortDirection == "DESC")
       {
          return list.OrderByDescending(e => property.GetValue(e, null));
       }
       else
       {
          throw new ArgumentOutOfRangeException();
       }
    }
    
    其中TypeHelper有一个静态方法,如:

    public static class TypeHelper
    {
        public static object GetPropertyValue( object obj, string name )
        {
            return obj == null ? null : obj.GetType()
                                           .GetProperty( name )
                                           .GetValue( obj, null );
        }
    }
    
    您可能还希望从中查看动态LINQ。您可以使用IEnumerable扩展将列表强制转换为IQueryable,然后使用动态链接OrderBy扩展

     list = list.AsQueryable().OrderBy( sortBy + " " + sortDirection );
    

    您可以做的一件事是更改
    排序
    ,以便更好地使用lambda

    public enum SortDirection { Ascending, Descending }
    public void Sort<TKey>(ref List<Employee> list,
                           Func<Employee, TKey> sorter, SortDirection direction)
    {
      if (direction == SortDirection.Ascending)
        list = list.OrderBy(sorter);
      else
        list = list.OrderByDescending(sorter);
    }
    

    Sort使用IComparable接口(如果类型实现了它)。 您可以通过实现自定义IComparer来避免ifs:

    class EmpComp : IComparer<Employee>
    {
        string fieldName;
        public EmpComp(string fieldName)
        {
            this.fieldName = fieldName;
        }
    
        public int Compare(Employee x, Employee y)
        {
            // compare x.fieldName and y.fieldName
        }
    }
    

    您可以使用反射来访问该属性

    list = list.OrderBy( x => TypeHelper.GetPropertyValue( x, sortBy ) )
               .ToList();
    
    public List<Employee> Sort(List<Employee> list, String sortBy, String sortDirection)
    {
       PropertyInfo property = list.GetType().GetGenericArguments()[0].
                                    GetType().GetProperty(sortBy);
    
       if (sortDirection == "ASC")
       {
          return list.OrderBy(e => property.GetValue(e, null));
       }
       if (sortDirection == "DESC")
       {
          return list.OrderByDescending(e => property.GetValue(e, null));
       }
       else
       {
          throw new ArgumentOutOfRangeException();
       }
    }
    
    公共列表排序(列表、字符串排序、字符串排序方向)
    {
    PropertyInfo property=list.GetType().GetGenericArguments()[0]。
    GetType().GetProperty(sortBy);
    如果(排序方向==“ASC”)
    {
    return list.OrderBy(e=>property.GetValue(e,null));
    }
    if(sortDirection==“DESC”)
    {
    return list.OrderByDescending(e=>property.GetValue(e,null));
    }
    其他的
    {
    抛出新ArgumentOutOfRangeException();
    }
    }
    
    注释

  • 你为什么通过引用来传递列表
  • 您应该使用枚举作为排序方向
  • 如果传递lambda表达式,则可以得到更干净的解决方案 指定要排序的属性,而不是将属性名称指定为字符串
  • 在我的示例中,list==null将导致NullReferenceException,您应该捕获这种情况

  • 通过表达式构建订单可以被读取

    不知羞耻地从链接中的页面中窃取:

    // First we define the parameter that we are going to use
    // in our OrderBy clause. This is the same as "(person =>"
    // in the example above.
    var param = Expression.Parameter(typeof(Person), "person");
    
    // Now we'll make our lambda function that returns the
    // "DateOfBirth" property by it's name.
    var mySortExpression = Expression.Lambda<Func<Person, object>>(Expression.Property(param, "DateOfBirth"), param);
    
    // Now I can sort my people list.
    Person[] sortedPeople = people.OrderBy(mySortExpression).ToArray();
    
    //首先定义要使用的参数
    //在我们的订货条款中。这与“(person=>”相同
    //在上面的例子中。
    var param=表达式参数(typeof(Person),“Person”);
    //现在我们将生成lambda函数,该函数返回
    //“DateOfBirth”属性的名称。
    var mySortExpression=Expression.Lambda(Expression.Property(param,“DateOfBirth”),param);
    //现在我可以对我的人员列表进行排序了。
    Person[]sortedPeople=people.OrderBy(mySortExpression.ToArray();
    
    不幸的是,Rashack提供的解决方案不适用于值类型(int、enum等)

    对于任何类型的属性,这是我找到的解决方案:

    public static Expression<Func<T, object>> GetLambdaExpressionFor<T>(this string sortColumn)
        {
            var type = typeof(T);
            var parameterExpression = Expression.Parameter(type, "x");
            var body = Expression.PropertyOrField(parameterExpression, sortColumn);
            var convertedBody = Expression.MakeUnary(ExpressionType.Convert, body, typeof(object));
    
            var expression = Expression.Lambda<Func<T, object>>(convertedBody, new[] { parameterExpression });
    
            return expression;
        }
    
    公共静态表达式GetLambdaExpressionFor(此字符串sortColumn)
    {
    var类型=类型(T);
    var parameterExpression=Expression.Parameter(类型为“x”);
    var body=Expression.PropertyOrField(parameterExpression,sortColumn);
    var-convertedBody=Expression.MakeUnary(ExpressionType.Convert,body,typeof(object));
    var expression=expression.Lambda(convertedBody,new[]{parameterExpression});
    返回表达式;
    }
    
    这就是我解决问题的方法:

    List<User> list = GetAllUsers();  //Private Method
    
    if (!sortAscending)
    {
        list = list
               .OrderBy(r => r.GetType().GetProperty(sortBy).GetValue(r,null))
               .ToList();
    }
    else
    {
        list = list
               .OrderByDescending(r => r.GetType().GetProperty(sortBy).GetValue(r,null))
               .ToList();
    }
    
    List List=GetAllUsers();//私有方法
    如果(!排序)
    {
    列表=列表
    .OrderBy(r=>r.GetType().GetProperty(sortBy).GetValue(r,null))
    .ToList();
    }
    其他的
    {
    列表=列表
    .OrderByDescending(r=>r.GetType().GetProperty(sortBy).GetValue(r,null))
    .ToList();
    }
    
    这可以通过以下方式完成

    list.Sort( (emp1,emp2)=>emp1.FirstName.CompareTo(emp2.FirstName) );
    
    NET framework正在将lambda
    (emp1,emp2)=>int
    转换为
    比较器。

    这具有强类型的优点

    如果需要降序/逆序,请反转参数

    list.Sort( (emp1,emp2)=>emp2.FirstName.CompareTo(emp1.FirstName) );
    

    添加到@Samuel和@bluish所做的操作中。这要短得多,因为在本例中不需要枚举。另外,当升序为所需结果时,您只能传递2个参数,而不是3个,因为true是第三个参数的默认答案

    public void Sort<TKey>(ref List<Person> list, Func<Person, TKey> sorter, bool isAscending = true)
    {
        list = isAscending ? list.OrderBy(sorter) : list.OrderByDescending(sorter);
    }
    
    public void Sort(参考列表列表,功能分类器,bool isAscending=true)
    {
    列表=isA
    
    List<User> list = GetAllUsers();  //Private Method
    
    if (!sortAscending)
    {
        list = list
               .OrderBy(r => r.GetType().GetProperty(sortBy).GetValue(r,null))
               .ToList();
    }
    else
    {
        list = list
               .OrderByDescending(r => r.GetType().GetProperty(sortBy).GetValue(r,null))
               .ToList();
    }
    
    list.Sort( (emp1,emp2)=>emp1.FirstName.CompareTo(emp2.FirstName) );
    
    list.Sort( (emp1,emp2)=>emp2.FirstName.CompareTo(emp1.FirstName) );
    
    public void Sort<TKey>(ref List<Person> list, Func<Person, TKey> sorter, bool isAscending = true)
    {
        list = isAscending ? list.OrderBy(sorter) : list.OrderByDescending(sorter);
    }
    
    private readonly Dictionary<string, Expression<Func<IuInternetUsers, object>>> _sortColumns = 
            new Dictionary<string, Expression<Func<IuInternetUsers, object>>>()
        {
            { nameof(ContactSearchItem.Id),             c => c.Id },
            { nameof(ContactSearchItem.FirstName),      c => c.FirstName },
            { nameof(ContactSearchItem.LastName),       c => c.LastName },
            { nameof(ContactSearchItem.Organization),   c => c.Company.Company },
            { nameof(ContactSearchItem.CustomerCode),   c => c.Company.Code },
            { nameof(ContactSearchItem.Country),        c => c.CountryNavigation.Code },
            { nameof(ContactSearchItem.City),           c => c.City },
            { nameof(ContactSearchItem.ModifiedDate),   c => c.ModifiedDate },
        };
    
        private IQueryable<IuInternetUsers> SetUpSort(IQueryable<IuInternetUsers> contacts, string sort, string sortDir)
        {
            if (string.IsNullOrEmpty(sort))
            {
                sort = nameof(ContactSearchItem.Id);
            }
    
            _sortColumns.TryGetValue(sort, out var sortColumn);
            if (sortColumn == null)
            {
                sortColumn = c => c.Id;
            }
    
            if (string.IsNullOrEmpty(sortDir) || sortDir == SortDirections.AscendingSort)
            {
                contacts = contacts.OrderBy(sortColumn);
            }
            else
            {
                contacts = contacts.OrderByDescending(sortColumn);
            }
    
            return contacts;
        }