在x - y排序中使用变量
本文关键字:变量 排序 | 更新日期: 2023-09-27 17:54:08
我现在有以下内容:
switch (Mysort)
{
case "reqDate":
lstDMV.Sort((x, y) => DateTime.Compare(x.RequestDate, y.RequestDate));
break;
case "notifDate":
lstDMV.Sort((x, y) => DateTime.Compare(x.NotifDate, y.NotifDate));
break;
case "dueDate":
lstDMV.Sort((x, y) => String.Compare(x.TargetDateShort, y.TargetDateShort));
break;
case "days":
lstDMV.Sort((x, y) => x.DaysLapsed.CompareTo(y.DaysLapsed));
break;
}
我想去掉case语句只做这样的事情:
lstDMV.Sort((x, y) => String.Compare(x.MySort, y.MySort));
case语句太大了,这会降低可读性。但是因为MySort
不包含在lstDMV
中它不起作用。有没有别的方法可以代入?
我当然会改变文本,以确保MySort
变量值与lstDMV
属性名称完全匹配。
我也试过以下方法,但没有成功:(
if (sort != "")
{
string xsort, ysort;
xsort = "x." + sort;
ysort = "y." + sort;
lstDMV.Sort((x, y) => String.Compare(xsort, ysort));
}
带有比较器Func的字典
public class YourDataClass {
public string RequestDate { get; set; }
public string NotifDate { get; set; }
.
.
.
}
public class Sorter<T> where T : YourDataClass {
private Dictionary<string, Func<T, T, int>> actions =
new Dictionary<string, Func<T, T, int>> {
{"reqDate", (x, y) => String.Compare(x.RequestDate, y.RequestDate)},
{"notifDate", (x, y) => String.Compare(x.NotifDate, y.NotifDate)}
};
public IEnumerable<T> Sort(IEnumerable<T> list, string howTo) {
var items = list.ToArray();
Array.Sort(items, (x, y) => actions[howTo](x, y));
return items;
}
}
public void Sample() {
var list = new List<YourDataClass>();
var sorter = new Sorter<YourDataClass>();
var sortedItems = sorter.Sort(list, "reqDate");
}
你可以使用反射,假设你所有的属性类型实现IComparable
:
private class Test
{
public DateTime RequestDate { get; set; }
public string Name { get; set; }
}
private static void Main(string[] args)
{
var list = new List<Test>
{
new Test
{
RequestDate = new DateTime(2012, 1, 1),
Name = "test"
},
new Test
{
RequestDate = new DateTime(2013, 1, 1),
Name = "a_test"
},
};
string mySort = "RequestDate";
list.Sort((x, y) =>
{
// Gets the property that match the name of the variable
var prop = typeof(Test).GetProperty(mySort);
var leftVal = (IComparable)prop.GetValue(x, null);
var rightVal = (IComparable)prop.GetValue(y, null);
return leftVal.CompareTo(rightVal);
});
Console.Read();
}
我不建议这样做,因为即使可能有更少的代码,它也比您目前拥有的switch
可读性差。
Linq和Reflection的组合,用3行代码解决你的问题。以下是概念证明:
public class Test
{
public string Name;
public int Age;
public DateTime Since;
}
void Main()
{
var tests = new Test[] {
new Test(){Name="Dude", Age=23, Since = new DateTime(2000,2,3)},
new Test(){Name="Guy", Age=29, Since = new DateTime(1999,3,4)},
new Test(){Name="Man", Age=34, Since = new DateTime(2008,11,5)},
new Test(){Name="Gentleman", Age=40, Since = new DateTime(2006,7,6)}
};
//up until here, all code was just test preparation.
//Here's the actual problem solving:
string fieldToOrderBy = "Since"; //just replace this to change order
FieldInfo myf = typeof(Test).GetField(fieldToOrderBy);
tests.OrderBy(t=>myf.GetValue(t)).Dump();
//the Dump() is because I ran this in LinqPad.
//Replace it by your favaorite way of inspecting an IEnumerable
}
请注意,字段信息是在排序之前获得的,以尝试提高性能。
我知道您的"SortBy"字符串不是字段名称,但这是问题的简单部分,您可以通过使用字典将SortBy字符串映射到FieldName来解决。
假设您的x
和y
是A
类型,您可以将映射存储在字典中:
var sortingFuncs = new Dictionary<string, Func<A, A, int>>();
排序方法的调用将不再需要switch
:
lstDMV.Sort(sortingFuncs[Mysort]);
但是,请注意,您将在其他地方得到几乎同样多的代码,因为必须在某个时刻填充字典。一个优点是这个过程可以更加动态,例如,您可以让插件添加自己的排序键以及比较函数。
另一方面,可能会造成非常轻微的性能损失,因为编译器可能无法再基于Mysort
的合格值来优化选择代码。
创建属性选择器:
Func<T, object> CreatePropSelector<T>(string propertyName)
{
var parameter = Expression.Parameter(typeof(T));
var body = Expression.Convert(Expression.PropertyOrField(parameter, propertyName), typeof(object));
return Expression.Lambda<Func<T, object>>(body, parameter).Compile();
}
并使用它按属性名排序序列:
lstDMV.OrderBy(CreatePropSelector<YourObjectType>(Mysort))
与其他Linq方法一样,它不会对列表进行排序,但会创建enumerator:
Mysort = "RequestDate";
foreach(var item in lstDMV.OrderBy(CreatePropSelector<YourObjectType>(Mysort)))
// sequence ordered by RequestDate property