什么是Comparer <t>类?</t>

时间:2010-05-16 09:38:10

标签: c#

如果您指定的类型已经实现Comparer<T>IComparable类的用途是什么?

如果我指定Comparer.Default,并且Customer已经实现了IComparable,那么为什么我会使用Comparer类?

7 个答案:

答案 0 :(得分:22)

因为你有时需要保持集合/有序队列按其他东西排序,然后是“自然”顺序或更多,然后存在一个自然顺序。

例如,如果您有平面线,您可能希望按以下方式对其进行排序:

  • 航班号
  • 目标
  • 时间
  • 优先级(某些航班可能会比其他航班延误更长时间)
  • ...

计算机中的任务可以通过以下方式安排:

  • 用户
  • 优先级(在调度程序中)
  • PID(正常比较)
  • ...

因此,即使在一个应用程序中,您也可能需要按不同的属性对对象进行排序。您不能通过int compareTo(Object)方法执行此操作,因为它不能在上下文之间进行区分。但是,您可以添加上下文,即实现CompareByPriority

答案 1 :(得分:18)

我认为你的问题是为什么一个基类似乎只有一个有用的方法,如果你直接实现了接口,它恰好与你实现的方法相同。如果我理解正确,我猜你是正确的,从Comparer<T>派生而不是直接实现IComparer<T>没有太大的好处,除了基类为你提供了一个通用的非泛型实现一个被覆盖的方法。

但如果你的问题是为什么同时拥有IComparer<T>IComparable<T>,那么 正如其他人所指出的那样,Comparer<T>允许您定义执行比较的不同方法。它是Strategy design pattern的实现。一个很好的例子是各种StringComparer属性,例如StringComparer.Ordinal,StringComparer.OrdinalIgnoreCase等。这允许您在IComparable<T>接口根本无法预料的不同情况下对字符串进行不同的排序。 / p>

但除了能够重新定义执行比较的方式之外,有时提供外部比较器是唯一可行的方法。例如,Windows窗体ListView类允许您为其排序逻辑指定IComparer。但ListViewItem不实现IComparable。因此,如果没有比较器策略知道如何操作,ListViewItem不可排序,因为它没有默认的IComparable实现。

所以在一天结束时,它只是另一个可扩展点,当你不是要排序的类型的作者(或者你需要多个排序策略)时,它允许你更灵活。)

<小时/> 编辑:回应你的评论如下:“如果我有一个实现IComparer(我自己的类)的类,这将允许我对任意数量的属性(自定义排序)进行排序,为什么我会打扰使用Comparer.Default“

也许一个例子会有所帮助。假设您正在编写一个扩展方法,用于检查给定值是否在范围之间。

public static bool Between<T>(this T value, T minValue, T maxValue) {

    var comparer = Comparer<T>.Default;

    int c1 = comparer.Compare(value, minValue);
    int c2 = comparer.Compare(value, maxValue);

    return (c1 >= 0 && c2 <= 0);

}

在这种情况下,我对类型T一无所知。它可以实现IComparable或者它可以实现IComparable<T>,或者它可以不实现,并且将抛出异常。这也允许我轻松地为这个方法添加一个重载,让调用者在他们自己的比较器中传递。但Comparer在这里派上用场,因为它让我得到一个未知类型的默认比较器,它可能会也可能不会实现通用或非通用的IComparable接口。

答案 2 :(得分:9)

该类型不需要实现IComparable,它可以是任何类型 - T 上没有约束:

public abstract class Comparer<T> : IComparer, IComparer<T>

您创建的新Comparer实现IComparer<T>和非通用IComparer,可用于集合的比较和排序。

您是对的:如果您的类型Customer实施IComparable,并且您不需要进行其他比较,则Comparer对您无用。 .net框架中的大多数类都可以接受IComparable<T>Comparer<T>,因此您可以使用其中任何一个。

但是,你总是这样认为是错误的。可以为非Comparable类型创建Comparer。请注意,以下是

public abstract class Comparer<T> : IComparer, IComparer<T>
                                    where T : IComparable, IComparable<T>

假设您有一个简单的类Person,并且想要对Persons的列表进行排序,那么最好用它来编写一个Comparer:

public class Person
{
    string Name { get; set; }
}

答案 3 :(得分:8)

这里有一些细微之处:

  • 它不仅支持IComparable<T> - 它还支持较旧(非通用)IComparable作为后备。这意味着它不能表达为(例如)通用约束
  • 它支持Nullable<T>,其中T具有可比性,即使Nullable<T>明显不是 IComparableIComparable<T>
  • 它可以防止通用类型约束的爆炸而不是要求它们 - 例如,List<T>可以提供Sort,即使它不坚持< / em>所有T都是可排序的;你会惊讶地发现通用约束会以多快的速度累积
  • 它允许您将 comparer 传递给任何需要比较器的现有API,只要您拥有的是可以比较的类型;大多数框架排序API(包括LINQ)将提供比较器支持

答案 4 :(得分:2)

public class Person
{
    public string LastName;
    public string FirstName;

}

public class Class2
{
    public void test()
    {
        List<Person> classList = new List<Person>();
        //add some data to the list
        PersonComparer comp = new PersonComparer();
        classList.Sort(comp);
    }
}

public class PersonComparer : Comparer<Person>
{

    public override int Compare(Person x, Person y)
    {
        int val = x.LastName.CompareTo(y.LastName);
        if (val == 0)
        {
            val = x.FirstName.CompareTo(y.FirstName);
        }
        return val;
    }
}

答案 5 :(得分:1)

如果某个类型实现了IComparable<T>,那么使用它几乎肯定比IComparable更好。对于值类型,IComparable<T>的性能通常比非泛型IComparable的性能要好得多。通过允许基于派生类型字段的排名,IComparable<T>可以使用可继承的引用类型,提供比IComparable更好的语义。

对于后一个好处的示例,假设一个具有属性ScheduleEvent的抽象基类EventTime,它通过排序IComparable<ScheduleEvent>来实现EventTime。派生类型包括带有消息字符串的ScheduledPopupMessageEvent,带有ScheduledGongEvent参数的GongVolume。具有相同ScheduleEvent的多个EventTime必须为IComparable<ScheduleEvent>.CompareTo报告零,因为没有安全且一致的方法来排名不同类型的ScheduleEvent,并且因为两个{{为了保持一致性,两者都报告自己相对于第三个人不必报告,必须将自己报告为相对于彼此不相称。另一方面,ScheduleEvent实施ScheduledGongEvent考虑IComparable<ScheduledGongEvent>以及GongVolumeEventTime同样执行此问题也不会有问题它的Message参数。

如果排序例程使用ScheduledPopupMessageEvent(如果存在),那么它很有用,但如果IComparable<T>不存在则能够回退到IComparable。然而,检查类是否实现IComparable<T>并选择适当的实现是否有点贵。幸运的是,一旦确定某个类型具有IComparable<T>实现,就可以依赖它来永远拥有一个类型;同样,如果发现某种类型没有这样的实现,它永远不会。此外,如果泛型类具有任何静态字段,则类型参数的每个组合将产生具有不同字段的不同类。因此,第一次使用特定类型参数T运行IComparable<T>时,它将存储它返回到静态字段的Comparer例程。如果再次使用相同类型运行Comparer<T>.Default,它将返回相同的比较器例程。虽然使用一个方法创建一个静态Comparer<T>类似乎很奇怪,但为每个类型Comparer<T>创建一个单独的Comparer<T>类提供了一个存储创建的比较例程的位置。

答案 6 :(得分:0)

Comparer<T>包含实际的比较方法。如果您想要以与IComparable实现中不同的方式比较对象,则可以使用它。