65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 LINQ 对 NUnit 项目的集合类进行排序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (17投票s)

2010年3月15日

CPOL

6分钟阅读

viewsIcon

23529

downloadIcon

209

提供了一个可以在 NUnit 项目中调用的静态类,用于对集合进行排序和比较。

引言

这是一个静态类,可用于根据对象的特定属性对自定义对象集合进行排序。这在需要比较两个相同类型且具有相同数量对象的集合时尤其有用,尤其当它们加载方式不同时。例如,在 NUnit 项目中,我们需要使用 Assert.AreEqual 来比较两个集合,一个集合是从数据库填充的,另一个集合是根据 XML 文件填充的,并且我们期望其值。此时,我们不知道对象是以何种顺序添加到集合中的,但两个集合包含相同数量的相同对象。当我们尝试比较它们时,由于对象顺序不同,测试会失败。使用 SortCollection 类,我们可以通过以相同方式对两个集合进行排序来消除这个问题。

必备组件

要运行本文提供的演示项目,您需要安装 Visual Studio 2008、Framework 3.5 和 NUnit(2.5 或更高版本)。

代码

我在本文中包含了一个演示项目,以展示 SortCollection 类如何使用。我将在文章后面讨论演示项目。首先,我想简要描述一下这个类的字段、方法和辅助方法。

Fields 区域,我们发现一个私有的枚举,它被 MinMaxItem 私有辅助方法项内部使用,以确定排序的类型:升序或降序。

Fields 区域包含一个私有的枚举

#region Fields
//===============================================
        private enum MinMax { Min, Max };
//===============================================
#endregion

我们继续讲辅助方法区域,并讨论它们的方法和作用。第一个公开的方法是私有的 Sort 方法。返回类型是 IEnumerable<TSource>TSource 可以是任何类型的对象;在我的演示中,我使用了一个名为 Deal 的对象。Sort 方法使用泛型。第一个泛型类型 TSource 是我们集合中对象的类型,第二个泛型类型 TValue 可以是我们用于排序集合的任何值类型。TValue 基本上是对象中的一个属性。在我的示例中,我使用了 Amount 属性,它是一个 double,但也可以是整数类型的 DealIDSort 方法有三个参数:第一个是我们要排序的集合,第二个是对 LINQ 在排序过程中内部使用的函数的委托,第三个是类型为 TValue 的比较器,它也是一个值类型。body 方法使用 LINQ 的 OrderBy 方法根据选择器和比较器对集合进行排序。返回类型是 LINQ 排序后的集合,稍后将使用 ConvertToCollection 方法将其转换为我们的 DealCollection 类型。

此区域的第二个方法是 MinMaxItem,它返回一个 TSource 类型的对象。我的 MinMaxItem 方法与标准的 LINQ Max 方法的一个区别是,Max 方法返回集合中的最大值,而我的方法返回包含最大值的对象。例如,如果我们看演示项目,我想要找到集合中最大的 Amount,如果我使用标准的 Max 方法,返回的值是 15000,但如果我使用我的方法,则返回包含 Amount 属性等于 15000 的对象。当,例如,在我们的 NUnit 测试中,一个测试通过处理交易来修改金额和/或其他属性时,这一点非常有用。稍后在另一个 NUnit 测试中,我们将交易金额和/或其他属性与测试交易对象属性进行比较,我们预先知道金额值或其他属性值应该是多少。与前面描述的方法相比,MinMaxitem 有一个额外的参数,即 MinMax 枚举类型。

#region Helpers // ======================================================
private static IEnumerable<TSource> Sort<TSource, TValue>(
    this IEnumerable<TSource> source,
    Func<TSource, TValue> selector,
    IComparer<TValue> comparer)
{
    try
    {
        IEnumerable<TSource> newSource = 
         source.OrderBy<TSource, TValue>(selector, comparer);
        return newSource;
    }
    catch (System.Exception exception)
     { throw new FormatException(exception.ToString()); }
}

//===============================================================
private static TSource MinMaxItem<TSource, TValue>(
    this IEnumerable<TSource> source,
    Func<TSource, TValue> selector,
    IComparer<TValue> comparer,
    MinMax element)
{
    try
    {
        TSource minMaxItem = default(TSource);
        TValue minMaxValue = default(TValue);

        using (var enumerator = source.GetEnumerator())
        {
            if (enumerator.MoveNext())
            {
                minMaxItem = enumerator.Current;
                minMaxValue = selector(minMaxItem);
                while (enumerator.MoveNext())
                {
                    TValue value = selector(enumerator.Current);
                    if (element == MinMax.Max ? 
                        comparer.Compare(value, minMaxValue) > 0 : 
                        comparer.Compare(value, minMaxValue) < 0)
                    {
                        minMaxItem = enumerator.Current;
                        minMaxValue = value;
                    }
                }
            }
        }
        return minMaxItem;
    }
    catch (System.Exception exception)
    { throw new FormatException(exception.ToString()); }           
}
//===============================================================
#endregion

Methods 区域公开了以下方法:MaxItemMinItemSortAscendingSortDescendingConvertToCollection。让我们逐个讨论这些方法,从 MaxItem 开始。MaxItem 公共函数返回一个 TSource 类型,在我们的例子中是 Deal 类型。此方法调用 MinMaxItem 私有方法,该方法返回具有我们提供的属性最大值的对象。MinItem 也是一样,区别是该方法返回具有我们提供的属性最小值的对象。SortAscendingSortDescending 非常相似;后者使用 Reverse 方法反转集合的顺序。最后一个要讨论的方法是 ConverToCollection 方法,它是 public 的,因此可以在其他情况下使用。此方法将 IEnumarable 集合转换为自定义集合。

#region Methods
//=================================================================
public static TSource MaxItem<TSource, TValue>(this 
       IEnumerable<TSource> source, Func<TSource, TValue> selector)
{
  return MinMaxItem<TSource, TValue>(source, selector, 
       Comparer<TValue>.Default, MinMax.Max);
}
//=================================================================
public static TSource MinItem<TSource, TValue>(this 
       IEnumerable<TSource> source, Func<TSource, TValue> selector)
{
  return MinMaxItem<TSource, TValue>(source, selector, 
         Comparer<TValue>.Default, MinMax.Min);
}
//=================================================================
public static C SortAscending<TSource, TValue, C>(this 
       IEnumerable<TSource> source, Func<TSource, TValue> selector)
where C : IList<TSource>, new()
{
return ConvertToCollection<C, TSource>(Sort<TSource, 
       TValue>(source, selector, Comparer<TValue>.Default));
}
//=================================================================
public static C SortDescending<TSource, TValue, C>(this 
       IEnumerable<TSource> source, Func<TSource, TValue> selector)
where C : IList<TSource>, new()
{
return ConvertToCollection<C, TSource>(Sort<TSource, TValue>(source, 
       selector, Comparer<TValue>.Default).Reverse());
}
//=================================================================
public static C ConvertToCollection<C, T>(
                this IEnumerable linqCollection)
where C : IList<T>, new()
{
C collection = new C();
//
foreach (var item in linqCollection)
{
collection.Add((T)item);
}
return collection;
}
//=================================================================
#endregion

使用代码

首先,我们需要看一下演示项目。让我们看看 SortedCollectionTest NUnit 方法以及 SortAscending 方法是如何被调用的。正如我们所见,SortAscending 有三个泛型类型需要我们在调用时提供。第一个泛型类型是对象的类型;在我们的例子中,这个类型是 Deal。下一个泛型类型是我们用来对集合进行排序的值类型;在我们的例子中是 double。我在这里使用 double 是因为我想根据 Deal 对象是 doubleAmount 属性来对集合进行排序。例如,如果我们按 DealID 对集合进行排序,我们也可以使用 int。第三个泛型是 DealCollection 类型,它将在排序后用于将 IEnumerable 类型转换为 DealCollection 类型。该方法接受一个参数:选择器 (deal => deal.Amount),它用于指定要排序集合的属性及其类型。

像这样调用 SortAscending 方法

sortedCollA = dealCollectionA.SortAscending<Deal, 
                    double, DealCollection>(deal => deal.Amount);
sortedCollB = dealCollectionB.SortAscending<Deal, 
                    double, DealCollection>(deal => deal.Amount);

结论

总而言之,开发人员可以使用现有的 LINQ 方法,如 OrderByOrderDescendingByCast 等,来实现相同的结果。在我看来,使用这个静态类可能有一些潜在的优势,例如

  • 对所有项目使用相同的类方法可提供一致性
  • 无需在项目中包含 using Linq 指令即可对集合进行排序或提取 Min 或 Max,从而使 intellisense 更简洁
  • SortAscendingSortDescending 会自动将 IEnumerator 类型集合转换为指定类型的集合,并返回新类型的集合

关注点

这里主要关注的点是,有时我们无法控制需要比较的集合是如何填充的,我们需要能够以某种方式对它们进行排序。这只是对集合进行排序的另一种方式。

您觉得怎么样?

如果您喜欢这篇文章,请为它投票。如果您有任何问题,请随时提问,或者如果您认为有什么可以做得更好,请说出来。感谢您的时间,我希望这篇文章能为开发人员带来一些有用的东西。

历史

  • V1.0: 2009/11/6
© . All rights reserved.