Reputation: 37
I have a and mini
class and a List<T>
thereof.
List<mini> result;
public class mini
{
public long SN;
public int PlayTime;
public string Date;
public int Score;
}
//The value is the Name of SN, PlayTime, Date, Score
string sortColumn;
string sortColumnDir; //asc, desc value
The caller can specify a member name to sort on, and I try to sort on the result value:
if("SN" == sortColumn)
var sortresult = result.OrderBy(c => c.SN).ToList<miniCompletion>();
else if("PlayTime" == Date)
var sortresult = result.OrderBy(c => c.PlayTime).ToList<miniCompletion>();
else if("PlayTime" == sortColumn)
var sortresult = result.OrderBy(c => c.PlayTime).ToList<miniCompletion>();
else if("Score" == sortColumn)
var sortresult = result.OrderBy(c => c.Score).ToList<miniCompletion>();
But this code is too inefficient, because it involves a lot of copy-pasting nearly duplicate code. And for sorting descendingly, the code doubles in size.
So I tried:
var sortresult = result.OrderBy(c => c.GetType().GetMember(sortColumn)[0].Name).ToList();
But the sort failed.
Upvotes: 1
Views: 461
Reputation: 7054
You can build your sorting lambda dynamically with linq expressions. Try this code:
var parameter = Expression.Parameter(typeof(mini), "c");
var member = Expression.PropertyOrField(parameter, sortColumn);
var cast = Expression.Convert(member, typeof(IComparable));
var lambda = Expression.Lambda<Func<mini, IComparable>>(cast, parameter);
now lambda
is an expression like c => (IComparable)c.Date
and can be compiled to Func<mini, IComparable>
:
var func = lambda.Compile();
At this moment you can sort your result
:
var sortedResult = sortColumnDir == "ASC"
? result.OrderBy(func)
: result.OrderByDescending(func);
You can find demo here
Please note, that this code is working because all fields in mini
are implementing IComparable interface
Upvotes: 0
Reputation: 17
Change this:
if("SN" == sortColumn) var sortresult = result.OrderBy(c => c.SN).ToList<miniCompletion>(); else if("PlayTime" == Date) var sortresult = result.OrderBy(c => c.PlayTime).ToList<miniCompletion>(); else if("PlayTime" == sortColumn) var sortresult = result.OrderBy(c => c.PlayTime).ToList<miniCompletion>(); else if("Score" == sortColumn) var sortresult = result.OrderBy(c => c.Score).ToList<miniCompletion>();
With enums for asc, desc and sortColumn like:
If(SortComun.SN == sortColumn)
Int comparison is faster and enums are cleaner. Also, you can try result.AsParallel().OrderBy() if you have too much registers.
About your second approach, try:
var sortResult = result.OrderBy(c => c.GetType().GetProperty(sortColumn).GetValue(c)).ToList();
You can test which approach suits you better. Having reflection as the most generic answer, the other one could be faster in an intensive environment.
If you test and share your tests result could be awesome.
Upvotes: 0
Reputation: 151654
The sort fails because you're sorting by the property name, not its value. The name is of course equal for all items.
You need to get the value. Use GetField(...).GetValue(c)
. If you can't use GetField()
(or you'd rather even use properties, not fields, so GetProperty()
), see How do I get the value of MemberInfo?.
Upvotes: 2