我的方法接收所有DataTables参数以按列单击对表进行排序。我从每个页面列表的控制器调用此方法。 我正在寻找一种更好的方法,像所有类型的通用方法一样:字符串, int ,十进制, double , bool (可空或不)。但是我无法找到它。
我目前的代码:
public List<T> OrderingList<T>(List<T> list, DataTablesParam model)
{
var iColumn = model.Order.FirstOrDefault().Column;
var property = typeof(T).GetProperty(model.Columns.ToArray()[iColumn].Data);
var param = Expression.Parameter(typeof(T));
var final = Expression.Property(param, property);
var isDirAsc = model.Order.FirstOrDefault().Dir.Equals("asc");
if (property.PropertyType == typeof(string))
{
var lambda = Expression.Lambda<Func<T, string>>(final, param).Compile();
return isDirAsc ? list.OrderBy(lambda).ToList() : list.OrderByDescending(lambda).ToList();
}
else if (property.PropertyType == typeof(int))
{
var lambda = Expression.Lambda<Func<T, int>>(final, param).Compile();
return isDirAsc ? list.OrderBy(lambda).ToList() : list.OrderByDescending(lambda).ToList();
}
else if (property.PropertyType == typeof(bool))
{
var lambda = Expression.Lambda<Func<T, bool>>(final, param).Compile();
return isDirAsc ? list.OrderBy(lambda).ToList() : list.OrderByDescending(lambda).ToList();
}
else if (property.PropertyType == typeof(decimal))
{
var lambda = Expression.Lambda<Func<T, decimal>>(final, param).Compile();
return isDirAsc ? list.OrderBy(lambda).ToList() : list.OrderByDescending(lambda).ToList();
}
else if (property.PropertyType == typeof(double))
{
var lambda = Expression.Lambda<Func<T, double>>(final, param).Compile();
return isDirAsc ? list.OrderBy(lambda).ToList() : list.OrderByDescending(lambda).ToList();
}
return list;
}
我想做这样的事情:(但这段代码不起作用)
public List<T> OrderingList<T>(List<T> list, DataTablesParam model)
{
var iColumn = model.Order.FirstOrDefault().Column;
var property = typeof(T).GetProperty(model.Columns.ToArray()[iColumn].Data);
var param = Expression.Parameter(typeof(T));
var final = Expression.Property(param, property);
var isDirAsc = model.Order.FirstOrDefault().Dir.Equals("asc");
var lambda = Expression.Lambda<Func<T, dynamic>>(final, param).Compile();
return isDirAsc ? list.OrderBy(lambda).ToList() : list.OrderByDescending(lambda).ToList();
}
答案 0 :(得分:1)
您只需使用反射调用Enumerable.OrderBy
方法即可。这样,您不必在编译时知道类型。为此,您只需要获取方法,并使用属性的类型创建泛型方法:
private IEnumerable<T> Sort<T> (List<T> list, string propertyName)
{
MethodInfo orderByMethod = typeof(Enumerable).GetMethods().First(mi => mi.Name == "OrderBy" && mi.GetParameters().Length == 2);
PropertyInfo pi = typeof(T).GetProperty(propertyName);
MethodInfo orderBy = orderByMethod.MakeGenericMethod(typeof(T), pi.PropertyType);
ParameterExpression param = Expression.Parameter(typeof(T));
Delegate accessor = Expression.Lambda(Expression.Property(param, pi), param).Compile();
return (IEnumerable<T>)orderBy.Invoke(null, new object[] { lst, accessor });
}
请注意,我抽象出了有关模型的内容,以保持此方法的通用性。它基本上可以通过指定属性名称按列表中的任何属性进行排序。您的原始方法将如下所示:
public List<T> OrderingList<T>(List<T> list, DataTablesParam model)
{
var iColumn = model.Order.FirstOrDefault().Column;
string propertyName = model.Columns.ToArray()[iColumn].Data;
return Sort(list, propertyName).ToList();
}
答案 1 :(得分:0)
Beyond just not being very generic, your solution also requires a lot of extra memory because you're copying the list with LINQ. You can avoid this using List.Sort
.
I would do:
static void SortBy<T>(List<T> list, MemberInfo member, bool desc)
{
Comparison<T> cmp = BuildComparer<T>(member, desc);
list.Sort(cmp);
}
static Comparison<T> BuildComparer<T>(MemberInfo member, bool desc)
{
var left = Expression.Parameter(typeof(T));
var right = Expression.Parameter(typeof(T));
Expression cmp = Expression.Call(
Expression.MakeMemberAccess(desc ? right : left, member),
"CompareTo",
Type.EmptyTypes,
Expression.MakeMemberAccess(desc ? left : right, member));
return Expression.Lambda<Comparison<T>>(cmp, left, right).Compile();
}
答案 2 :(得分:0)
It's works fine for me: (Thanks @Poke)
https://stackoverflow.com/a/31393168/5112444
My final method:
private IEnumerable<T> Sort<T>(IEnumerable<T> list, string propertyName, bool isAsc)
{
MethodInfo orderByMethod = typeof(Enumerable).GetMethods().First(mi => mi.Name == (isAsc ? "OrderBy" : "OrderByDescending") && mi.GetParameters().Length == 2);
PropertyInfo pi = typeof(T).GetProperty(propertyName);
MethodInfo orderBy = orderByMethod.MakeGenericMethod(typeof(T), pi.PropertyType);
ParameterExpression param = Expression.Parameter(typeof(T));
Delegate accessor = Expression.Lambda(Expression.Call(param, pi.GetGetMethod()), param).Compile();
return (IEnumerable<T>)orderBy.Invoke(null, new object[] { list, accessor });
}
答案 3 :(得分:0)
您建议的方法几乎可行。您需要更改两件事才能使其正常工作:
public List<T> OrderingList<T>(List<T> list, DataTablesParam model)
{
var iColumn = model.Order.FirstOrDefault().Column;
var property = typeof(T).GetProperty(model.Columns.ToArray()[iColumn].Data);
var param = Expression.Parameter(typeof(T), "p");
Expression final = Expression.Property(param, property);
// Boxing of value types
if (property.PropertyType.IsValueType) {
final = Expression.MakeUnary(ExpressionType.Convert, final, typeof(object));
}
var isDirAsc = model.Order.FirstOrDefault().Dir.Equals("asc");
// VVVVVV
var lambda = Expression.Lambda<Func<T, object>>(final, param).Compile();
return isDirAsc
? list.OrderBy(lambda).ToList()
: list.OrderByDescending(lambda).ToList();
}
dynamic
使用object
,因为每种类型都是object
。如果您有值类型,则需要装箱操作,即必须将值强制转换为对象(object)i
。这是通过一元转换操作完成的:
Expression final = Expression.Property(param, property);
if (property.PropertyType.IsValueType) {
final = Expression.MakeUnary(ExpressionType.Convert, final, typeof(object));
}
另请注意,final显式声明为Expression
,因为表达式类型可能会从属性更改为一元表达式。
答案 4 :(得分:0)
我找到了一个更好的方法来做到这一点。我必须做3个步骤:
1 - 在项目中添加“Linq Dynamic”软件包:
Install-Package System.Linq.Dynamic.Library
2 - 在Class中导入包:
using System.Linq.Dynamic;
3 - 按字符串名称列出的订单列表:
list.OrderBy(stringPropertyName); //asc
list.OrderBy(stringPropertyName + " descending"); //des
它非常适合我。