这个问题涉及两种基本相同代码的不同实现。
首先,使用委托创建一个比较方法,该方法可以在排序对象集合时用作参数:
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);
我的问题是:
答案 0 :(得分:7)
在性能方面,这两种方案都没有任何优势。这真的是一个方便和代码可维护性的问题。选择您喜欢的选项。话虽如此,有问题的方法会略微限制您的选择。
您可以使用List<T>.Sort
IComparer<T>
界面,这样就可以不重复代码。
不幸的是,BinarySearch没有使用Comparison<T>
实现选项,因此您无法使用Comparison<T>
委托作为该方法(至少不能直接使用)。
如果你真的想对两者都使用Comparison<T>
,你可以制作一个通用的IComparer<T>
实现,在其构造函数中使用Comparison<T>
委托,并实现IComparer<T>
。< / p>
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);
}
}
答案 1 :(得分:6)
接受Comparison<T>
而不是IComparer<T>
的最大好处可能是编写匿名方法的能力。如果我有一个List<MyClass>
,其中MyClass
包含应该用于排序的ID
属性,我可以这样写:
myList.Sort((c1, c2) => c1.ID.CompareTo(c2.ID));
这比编写整个IComparer<MyClass>
实现更方便。
我不确定接受IComparer<T>
确实有任何主要优势,除了与遗留代码(包括.NET Framework类)的兼容性。 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));
答案 2 :(得分:1)
委托技术很短(lambda表达式可能更短),所以如果你的目标是更短的代码,那么这是一个优势。
但是,实现IComparer(及其通用等价物)会使您的代码更易于测试:您可以在比较类/方法中添加一些单元测试。
此外,在组合两个或更多比较器并将它们组合为新的比较器时,您可以重用比较器实现。使用匿名委托重用代码更难实现。
所以,总结一下:
匿名代表:更短(也许更干净)的代码
明确实现:可测试性和代码重用。
答案 3 :(得分: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)))
}
答案 4 :(得分:0)
他们真正满足了不同的需求:
IComparable
对订购的对象很有用。实数应该是可比较的,但复数不能 - 它是不明确的。
IComparer
允许定义可重用,封装良好的比较器。如果比较需要了解一些其他信息,这将特别有用。例如,您可能希望比较不同时区的日期和时间。这可能很复杂,应该使用单独的比较器。
比较方法是针对简单的比较操作而进行的,这些比较操作不够复杂,无法重复使用,例如:按名字排序客户列表。这是简单的操作,因此不需要额外的数据。同样,这不是对象固有的,因为对象不是以任何方式自然排序的。
最后,有IEquatable
,如果您的Equals
方法只能决定两个对象是否相等,但是如果没有“更大”和“更小”的概念,这可能很重要,例如复数或空间中的向量。