首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >比较对象的不同实现的优缺点

比较对象的不同实现的优缺点
EN

Stack Overflow用户
提问于 2010-03-22 02:56:59
回答 5查看 2.5K关注 0票数 18

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

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

代码语言:javascript
复制
class Foo
{
    public static Comparison<Foo> BarComparison = delegate(Foo foo1, Foo foo2)
    {
        return foo1.Bar.CompareTo(foo2.Bar);
    };
}

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

代码语言:javascript
复制
List<Foo> fooList = new List<Foo>();
fooList.Sort(BarComparison);

第二,使用IComparer:

代码语言:javascript
复制
public class BarComparer : IComparer<Foo>
{
    public int Compare(Foo foo1, Foo foo2)
    {
        return foo1.Bar.CompareTo(foo2.Bar);
    }
}

当我想对Foo对象集合中的Foo对象进行二进制搜索时,我会使用上面的代码。例如:

代码语言:javascript
复制
BarComparer comparer = new BarComparer();
List<Foo> fooList = new List<Foo>();
Foo foo = new Foo();
int index = fooList.BinarySearch(foo, comparer);

我的问题是:

  • 这些实现的优点和缺点是什么?
  • 利用这些实现的其他方法是什么?
  • 是否有一种方法可以将这些实现组合在一起,使我不需要重复代码?
  • 我是否可以仅使用其中的一个实现来实现二进制搜索和备用集合排序
EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2010-03-22 03:10:30

IComparer<T>相比,接受Comparison<T>的最大优势可能是能够编写匿名方法。假设我有一个List<MyClass>,其中MyClass包含一个应该用于排序的ID属性,我可以这样写:

代码语言:javascript
复制
myList.Sort((c1, c2) => c1.ID.CompareTo(c2.ID));

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

除了与遗留代码(包括.NET IComparer<T>类)兼容之外,我不确定接受框架是否真的有什么主要优势。Comparer<T>.Default属性只对基本类型有用;其他所有内容通常都需要进行额外的编码工作。

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

代码语言:javascript
复制
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);
    }
}

这允许编写代码,例如:

代码语言:javascript
复制
myList.BinarySearch(item,
    new AnonymousComparer<MyClass>(x.ID.CompareTo(y.ID)));

它不是很漂亮,但它节省了一些时间。

我的另一个有用的类是这个:

代码语言:javascript
复制
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>设计的代码编写为:

代码语言:javascript
复制
myList.BinarySearch(item, new PropertyComparer<MyClass, int>(c => c.ID));
票数 6
EN

Stack Overflow用户

发布于 2010-03-22 03:03:48

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

您可以使用List.SortIComparer<T>接口,这将允许您不复制代码。

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

如果您确实想将Comparison<T>用于这两者,您可以创建一个通用的IComparer<T>实现,该实现在其构造函数中接受一个Comparison<T>委托,并实现IComparer<T>

代码语言:javascript
复制
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);
    }
}
票数 7
EN

Stack Overflow用户

发布于 2010-03-22 03:05:02

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

但是,实现IComparer (和它的泛型等效项)会使代码更具可测试性:您可以在比较类/方法中添加一些单元测试。

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

所以,总结一下:

匿名委派:更短(也许更干净)的代码

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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2488298

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档