.NET中的动态排序解决方案

在.NET开发中,经常需要对对象列表进行排序,这类似于SQL中的排序操作。例如,可能需要根据一个或多个属性动态地对人员对象列表进行排序。在这种情况下,可能会想到使用IComparer和IComparer接口来创建一个通用的比较器。Peter Provost在他的文章“Generic Property Comparer Class”中介绍了如何在.NET 1.1中使用IComparer实现一个通用比较器。尽管Peter的解决方案在.NET 1.1的背景下是可行的,但在.NET 2.0的环境中,它存在一些不足之处,例如它只能按一个属性排序,只能进行升序排序,不是强类型的,而且速度很慢。

考虑到性能的重要性,没有人愿意在排序时等待5秒钟。因此,目标是创建一个既动态又高效的排序解决方案。理想情况下,用户应该能够输入一个类似SQL "Order By"语句的字符串,例如“FirstName, Age DESC, LastName”。

第一个解决方案是一个使用泛型的比较器,它能够对任意数量的属性进行升序和降序排序。这个解决方案满足了大多数重要需求,但性能测试表明,这个解决方案也不是一个可行的选择。为了满足需求,似乎需要动态生成一些特定的代码。幸运的是,.NET 2.0引入了一个名为“动态方法”的新特性。

“动态方法”是一种在运行时内存中创建的方法。在这里不深入讨论这个主题,但强烈推荐查看相关资料。基本上,所做的是在内存中编译一个强类型的“Compare”方法,并使用委托调用它。正如在下一节中将看到的,这是一种非常强大的方法,可以在保持完全动态的同时获得一个强类型的方法,并且速度非常快。

对最常用的排序方法以及Peter Provost的解决方案进行了一些性能测试。结果如下: 图1:对10到100000个项目进行排序的时间。 如所见,动态比较器解决方案是排序自定义实体列表的好方法。动态比较器与硬编码比较器并列第一。硬编码比较器似乎开始时速度较慢,但这只是一个测量误差。这清楚地表明,从运行时的角度来看,用于排序的动态方法与硬编码方法非常相似,只是通过委托调用它。

排在第二位的是弱类型和强类型数据集。它们都遵循相同的线,比其他方法更陡峭。这意味着它们的速度比其他方法下降得更快。有些人可能认为强类型数据集的排序速度应该比弱类型数据集快,但请再想一想……强类型数据集只是弱类型数据集的重载版本,所以实际上,它使用的排序方法与弱类型版本相同。值得一提的是,与常规列表相比,向数据集填充10000个或更多项目的速度要慢得多。

最后,排在第三位的是泛型类比较器。正如所见,它比其他排序方法慢得多。这并不是因为它设计得不好或编码得不好,而是因为.NET 1.1既不支持泛型也不支持动态方法。

那么如何使用这个比较器呢?非常简单: string orderBy = "FirstName, LastName DESC"; DynamicComparer<Person> comparer = new DynamicComparer<Person>(orderBy); persons.Sort(comparer.Compare); 1. 声明一个排序字符串或SortProperty数组。 注意:这可以由自定义编辑网格创建和维护。 2. 使用泛型构造函数声明一个DynamicComparer类的实例。 3. 在List<Person>上调用Sort方法。

幕后发生了什么?让从前面示例的第2行开始。在实例化DynamicComparer时,类首先会将输入字符串解析为SortProperty数组。然后,这些SortProperties将针对在实例化DynamicComparer时指定的类型进行验证,在本例中是“Person”。这个验证检查Person类是否为public,是否有名为“FirstName”和“LastName”的公共属性,这些属性是否可读,以及这些属性的类型是否实现了IComparable接口。如果任何验证失败,类将抛出异常。如果验证成功,类将使用指定的SortProperties生成一个动态方法,并实例化一个指向该方法的内部委托。然后,DynamicComparer上的“Compare”方法将能够在调用时调用这个委托。这意味着比较器已经准备好使用了。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485