随着实体的概念的流行,DataTable正在逐渐的淡出我们的视野,但是,有些时候,DataTable很容易做的一件事情,却让我们不得不头痛起来——按指定列排序
如果数据是来源于一个sql语句,那么,我们可以把这个问题转嫁给sql,让DB来解决这个问题。
但是,如果数据来源于某些不支持排序(例如webservice并且没有提供排序功能),或者其他复杂情况,这样就不得不依赖于.net的集合排序。可是,如果一个系统有10个这样的类,平均每个类有10个属性可能被选择,那么就意味着有100个排序方法需要被写(c#3.0的Lambda表达式当然可以为我们省下很多力气)。如果,有一个方法直接输入属性的名称,就返回一个按照对应属性的比较器,岂不是更容易?
基于这个想法,再加上效率的考量,用Emit写了简单的单一属性的使用默认比较器的排序:

Code
1
public static class Extensions
2
{
3
public static void SortByProperty<T>(this T[] array, string propName)
4
{
5
Array.Sort(array, Inner<T>.GetComparison(propName));
6
}
7
8
public static void SortByProperty<T>(this List<T> list, string propName)
9
{
10
list.Sort(Inner<T>.GetComparison(propName));
11
}
12
13
private static class Inner<T>
14
{
15
private static Dictionary<string, Comparison<T>> m_cache =
16
new Dictionary<string, Comparison<T>>();
17
18
internal static Comparison<T> GetComparison(string propName)
19
{
20
Comparison<T> comparison;
21
if (!m_cache.TryGetValue(propName, out comparison))
22
m_cache[propName] = comparison = GetComparisonNoCache(propName);
23
return comparison;
24
}
25
26
private static Comparison<T> GetComparisonNoCache(string propName)
27
{
28
bool isValueType = typeof(T).IsValueType;
29
var prop = typeof(T).GetProperty(propName);
30
var comparerType = typeof(Comparer<>).MakeGenericType(prop.PropertyType);
31
DynamicMethod dm = new DynamicMethod(string.Empty, typeof(int),
32
new Type[]
{ typeof(T), typeof(T) }, typeof(T));
33
var il = dm.GetILGenerator();
34
il.Emit(OpCodes.Call, comparerType.GetProperty("Default").GetGetMethod());
35
if (isValueType)
36
{
37
il.Emit(OpCodes.Ldarga_S, 0);
38
il.Emit(OpCodes.Call, prop.GetGetMethod());
39
il.Emit(OpCodes.Ldarga_S, 1);
40
il.Emit(OpCodes.Call, prop.GetGetMethod());
41
}
42
else
43
{
44
il.Emit(OpCodes.Ldarg_0);
45
il.Emit(OpCodes.Callvirt, prop.GetGetMethod());
46
il.Emit(OpCodes.Ldarg_1);
47
il.Emit(OpCodes.Callvirt, prop.GetGetMethod());
48
}
49
il.Emit(OpCodes.Callvirt, comparerType.GetMethod("Compare"));
50
il.Emit(OpCodes.Ret);
51
return (Comparison<T>)dm.CreateDelegate(typeof(Comparison<T>));
52
}
53
}
54
}
55
因为,考虑到经常需要排序可空类型(例如:int?),所以,使用Comparer<T>.Default的Compare方法,当然性能上略有损失,在属性是int的情况下,与直接用Lambda相比,性能大概是5:6,也就是说在数组相对比较大时,如果用Lambda需要5s,那么用这个方法可能就需要6s。功能上来说,由于无法传入比较器,排序功能上比较弱,只能应付用属性的默认比较器的排序的情况。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述