[C#] Lambda / Linq를 사용하여 객체에 대한 목록 정렬

문자열에 “속성 정렬”의 이름이 있습니다. Lambda / Linq를 사용하여 객체 목록을 정렬해야합니다.

전의:

public class Employee
{
  public string FirstName {set; get;}
  public string LastName {set; get;}
  public DateTime DOB {set; get;}
}


public void Sort(ref List<Employee> list, string sortBy, string sortDirection)
{
  //Example data:
  //sortBy = "FirstName"
  //sortDirection = "ASC" or "DESC"

  if (sortBy == "FirstName")
  {
    list = list.OrderBy(x => x.FirstName).toList();
  }

}
  1. 필드 이름 (sortBy)을 확인하기 위해 많은 if를 사용하는 대신 정렬을 수행하는 더 확실한 방법이 있습니까?
  2. 정렬이 데이터 유형을 알고 있습니까?


답변

이것은 다음과 같이 수행 할 수 있습니다

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

.NET 프레임 워크는 람다 (emp1,emp2)=>intComparer<Employee>.

이것은 강력하게 타이핑되는 장점이 있습니다.


답변

당신이 할 수있는 한 가지는 Sort람다를 더 잘 사용하도록 변경 하는 것입니다.

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메소드를 호출 할 때 정렬 할 필드를 지정할 수 있습니다 .

Sort(ref employees, e => e.DOB, SortDirection.Descending);


답변

Reflection을 사용하여 속성 값을 얻을 수 있습니다.

list = list.OrderBy( x => TypeHelper.GetPropertyValue( x, sortBy ) )
           .ToList();

TypeHelper에는 다음과 같은 정적 메소드가 있습니다.

public static class TypeHelper
{
    public static object GetPropertyValue( object obj, string name )
    {
        return obj == null ? null : obj.GetType()
                                       .GetProperty( name )
                                       .GetValue( obj, null );
    }
}

VS2008 샘플 라이브러리 에서 Dynamic LINQ를 볼 수도 있습니다 . IEnumerable 확장을 사용하여 List를 IQueryable로 캐스팅 한 다음 Dynamic link OrderBy 확장을 사용할 수 있습니다.

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


답변

이것이 내 문제를 해결 한 방법입니다.

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();
}


답변

식으로 주문 작성은 여기에서 읽을 수 있습니다

링크의 페이지에서 뻔뻔스럽게 도난당했습니다.

// 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();


답변

리플렉션을 사용하여 속성에 액세스 할 수 있습니다.

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();
   }
}

노트

  1. 왜 목록을 참조로 전달합니까?
  2. 정렬 방향으로 열거 형을 사용해야합니다.
  3. 속성 이름 대신 문자열로 정렬 할 속성을 지정하는 람다 식을 전달하면 훨씬 깨끗한 솔루션을 얻을 수 있습니다.
  4. 내 예제 list == null에서 NullReferenceException이 발생 하므로이 경우를 포착해야합니다.

답변

유형이 구현하는 경우 Sort는 IComparable 인터페이스를 사용합니다. 또한 사용자 정의 IComparer를 구현하여 if를 피할 수 있습니다.

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.Sort(new EmpComp(sortBy));