18

这个问题涉及基本相同代码的两种不同实现。

首先,使用委托创建一个比较方法,该方法可以在对对象集合进行排序时用作参数:

class Foo
{
    public static Comparison<Foo> BarComparison = delegate(Foo foo1, Foo foo2)
    {
        return foo1.Bar.CompareTo(foo2.Bar);
    };
}

当我想以一种与我的 CompareTo 函数提供的方式不同的方式对 Foo 对象集合进行排序时,我会使用上述方法。例如:

List<Foo> fooList = new List<Foo>();
fooList.Sort(BarComparison);

二、使用IComparer:

public class BarComparer : IComparer<Foo>
{
    public int Compare(Foo foo1, Foo foo2)
    {
        return foo1.Bar.CompareTo(foo2.Bar);
    }
}

当我想在 Foo 对象集合中对 Foo 对象进行二进制搜索时,我使用上述方法。例如:

BarComparer comparer = new BarComparer();
List<Foo> fooList = new List<Foo>();
Foo foo = new Foo();
int index = fooList.BinarySearch(foo, comparer);

我的问题是:

  • 这些实现中的每一个的优点和缺点是什么?
  • 还有哪些方法可以利用这些实现中的每一个?
  • 有没有办法以不需要复制代码的方式组合这些实现?
  • 我可以只使用其中一种实现来实现二进制搜索和替代集合排序吗?
4

5 回答 5

7

就性能而言,这两种选择都没有任何优势。这实际上是方便性和代码可维护性的问题。选择您喜欢的选项。话虽如此,所讨论的方法会稍微限制您的选择。

您可以使用IComparer<T>接口 for List<T>.Sort,这将允许您不重复代码。

不幸的是,BinarySearch没有使用 a 实现选项Comparison<T>,因此您不能Comparison<T>为该方法使用委托(至少不能直接使用)。

如果您真的想同时使用Comparison<T>两者,您可以创建一个通用IComparer<T>实现,Comparison<T>在其构造函数中接受一个委托,并实现IComparer<T>.

public class ComparisonComparer<T> : IComparer<T>
{
    private Comparison<T> method;
    public ComparisonComparer(Comparison<T> comparison)
    {
       this.method = comparison;
    }

    public int Compare(T arg1, T arg2)
    {
        return method(arg1, arg2);
    }
}
于 2010-03-21T19:03:48.447 回答
6

接受 aComparison<T>而不是 an的最大优势可能IComparer<T>是能够编写匿名方法。如果我有,比方说,List<MyClass>其中MyClass包含一个ID应该用于排序的属性,我可以写:

myList.Sort((c1, c2) => c1.ID.CompareTo(c2.ID));

这比编写整个IComparer<MyClass>实现要方便得多。

IComparer<T>除了与遗留代码(包括 .NET Framework 类)的兼容性之外,我不确定接受 a是否真的有任何主要优势。该Comparer<T>.Default属性仅对原始类型真正有用;其他一切通常都需要额外的工作来编写代码。

当我需要使用 时IComparer<T>,为了避免代码重复,我通常做的一件事是创建一个通用比较器,如下所示:

public class AnonymousComparer<T> : IComparer<T>
{
    private Comparison<T> comparison;

    public AnonymousComparer(Comparison<T> comparison)
    {
        if (comparison == null)
            throw new ArgumentNullException("comparison");
        this.comparison = comparison;
    }

    public int Compare(T x, T y)
    {
        return comparison(x, y);
    }
}

这允许编写代码,例如:

myList.BinarySearch(item,
    new AnonymousComparer<MyClass>(x.ID.CompareTo(y.ID)));

它并不完全漂亮,但它节省了一些时间。

我上的另一个有用的课程是这个:

public class PropertyComparer<T, TProp> : IComparer<T>
    where TProp : IComparable
{
    private Func<T, TProp> func;

    public PropertyComparer(Func<T, TProp> func)
    {
        if (func == null)
            throw new ArgumentNullException("func");
        this.func = func;
    }

    public int Compare(T x, T y)
    {
        TProp px = func(x);
        TProp py = func(y);
        return px.CompareTo(py);
    }
}

您可以编写以下代码IComparer<T>

myList.BinarySearch(item, new PropertyComparer<MyClass, int>(c => c.ID));
于 2010-03-21T19:10:30.353 回答
1

委托技术非​​常短(lambda 表达式可能更短),因此如果您的目标是更短的代码,那么这是一个优势。

但是,实现 IComparer(及其通用等价物)使您的代码更具可测试性:您可以在比较类/方法中添加一些单元测试。

此外,您可以在组合两个或多个比较器并将它们组合为新比较器时重用您的比较器实现。匿名委托的代码重用更难实现。

所以,总结一下:

匿名代表:更短(也许更简洁)的代码

显式实现:可测试性和代码重用。

于 2010-03-21T19:05:02.283 回答
0

在您的情况下,拥有IComparer<T>过度Comparision<T>委托的优点是您也可以将其用于 Sort 方法,因此您根本不需要Comparison委托版本。

您可以做的另一件有用的事情是实现这样的委托IComparer<T>实现:

public class DelegatedComparer<T> : IComparer<T>
{
  Func<T,T,int> _comparision;
  public DelegatedComparer(Func<T,T,int> comparision)
  {
    _comparision = comparision;
  }
  public int Compare(T a,T b) { return _comparision(a,b); }
}

list.Sort(new DelegatedComparer<Foo>((foo1,foo2)=>foo1.Bar.CompareTo(foo2.Bar));

和更高级的版本:

public class PropertyDelegatorComparer<TSource,TProjected> : DelegatedComparer<TSource>
{
  PropertyDelegatorComparer(Func<TSource,TProjected> projection)
    : base((a,b)=>projection(a).CompareTo(projection(b)))
}
于 2010-03-21T19:04:20.100 回答
0

他们真正解决了不同的需求:

IComparable对于有序的对象很有用。实数应该具有可比性,但复数不能——它的定义不明确。

IComparer允许定义可重用、封装良好的比较器。如果比较需要了解一些附加信息,这尤其有用。例如,您可能想要比较不同时区的日期和时间。这可能很复杂,为此应使用单独的比较器。

一种比较方法是为简单的比较操作而设计的,这些操作不够复杂,以至于可重用性不会受到任何关注,例如,按客户的名字对客户列表进行排序。这是简单的操作,因此不需要额外的数据。同样,这不是对象固有的,因为对象不是以任何方式自然排序的。

最后,如果您的方法只能确定两个对象是否相等,但如果没有“更大”和“更小”的概念,例如复数或空间中的向量IEquatable,这可能很重要。Equals

于 2010-03-21T19:05:38.427 回答