代码改变世界

快速创建 IEqualityComparer<T> 和 IComparer<T> 的实例

2011-08-02 21:14  鹤冲天  阅读(11605)  评论(16编辑  收藏  举报

几篇相关文章:《Linq的Distinct太不给力了》、《c# 扩展方法奇思妙用基础篇八:Distinct 扩展》、《何止 Linq 的 Distinct 不给力》,建议先看下。

.net 中 IEqualityComparer<T> 和 IComparer<T> 经常在 Linq 和 一些泛型集合、泛型字典类中用作参数。不过因其复杂性,包含 IEqualityComparer<T> 或 IComparer<T> 类型参数的函数一般使用频度不高。
尽管如此,有些情况下确非用不可,不得不创建一些新的类来实现 IEqualityComparer<T> 或 IComparer<T> 接口。不但增加了代码量,还对程序结构产生影响,新加入的类命名、放置、共用都是问题。
因此,我们期望能简单快速直接的创建 IEqualityComparer<T> 和 IComparer<T> 的实例。

本文给出两个实用类来实现这个目标,实现原理日后另撰文详述。

快速创建 IEqualityComparer<T> 的实例

我前一篇文章 《何止 Linq 的 Distinct 不给力》讨论的就是这个话题,这里就不再重复了,直接将 《何止 Distinct 不给力》一文中的总结出的实用类给出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public static class Equality<T>
{
    public static IEqualityComparer<T> CreateComparer<V>(Func<T, V> keySelector)
    {
        return new CommonEqualityComparer<V>(keySelector);
    }
    public static IEqualityComparer<T> CreateComparer<V>(Func<T, V> keySelector, IEqualityComparer<V> comparer)
    {
        return new CommonEqualityComparer<V>(keySelector, comparer);
    }

    class CommonEqualityComparer<V> : IEqualityComparer<T>
    {
        private Func<T, V> keySelector;
        private IEqualityComparer<V> comparer;

        public CommonEqualityComparer(Func<T, V> keySelector, IEqualityComparer<V> comparer)
        {
            this.keySelector = keySelector;
            this.comparer = comparer;
        }
        public CommonEqualityComparer(Func<T, V> keySelector)
            : this(keySelector, EqualityComparer<V>.Default)
        { }

        public bool Equals(T x, T y)
        {
            return comparer.Equals(keySelector(x), keySelector(y));
        }
        public int GetHashCode(T obj)
        {
            return comparer.GetHashCode(keySelector(obj));
        }
    }
}

Equality<T> 代码比较简洁,其中的关键是 EqualityComparer<V> 类和它的 Default 属性(有时间专门写篇文章来讲解)

使用示例:

1
2
3
4
var equalityComparer1 = Equality<Person>.CreateComparer(p => p.ID);
var equalityComparer2 = Equality<Person>.CreateComparer(p => p.Name);
var equalityComparer3 = Equality<Person>.CreateComparer(p => p.Birthday.Year);
var equalityComparer4 = Equality<Person>.CreateComparer(p => p.Name, StringComparer.CurrentCultureIgnoreCase);

Person 是一个简单的实体类:

1
2
3
4
5
6
class Person
{
    public int ID { get; set; }
    public string Name { get; set; }
    public DateTime Birthday { get; set; }
}

快速创建 IComparer<T> 的实例

参照上面的代码,照猫画虎,很容易写出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public static class Comparison<T>
{
    public static IComparer<T> CreateComparer<V>(Func<T, V> keySelector)
    {
        return new CommonComparer<V>(keySelector);
    }
    public static IComparer<T> CreateComparer<V>(Func<T, V> keySelector, IComparer<V> comparer)
    {
        return new CommonComparer<V>(keySelector, comparer);
    }

    class CommonComparer<V> : IComparer<T>
    {
        private Func<T, V> keySelector;
        private IComparer<V> comparer;

        public CommonComparer(Func<T, V> keySelector, IComparer<V> comparer)
        {
            this.keySelector = keySelector;
            this.comparer = comparer;
        }
        public CommonComparer(Func<T, V> keySelector)
            : this(keySelector, Comparer<V>.Default)
        { }

        public int Compare(T x, T y)
        {
            return comparer.Compare(keySelector(x), keySelector(y));
        }
    }
}

后注:这个类的名字起得不好,和 System.Comparison<T> 重名了,使用时最好改成其它名称如:ComparisonHelper<T>。

类似,Comparison<T> 的关键是 Comparer<V> 类和它的 Default 属性。

使用也是极其相似:

1
2
3
3
var comparer1 = Comparison<Person>.CreateComparer(p => p.ID);
var comparer2 = Comparison<Person>.CreateComparer(p => p.Name);
var comparer3 = Comparison<Person>.CreateComparer(p => p.Birthday.Year);
var comparer4 = Comparison<Person>.CreateComparer(p => p.Name, StringComparer.CurrentCultureIgnoreCase);

总结

借助本文中的 Equality<T> 和 Comparison<T>,可以不必引入新的类、不必自己实现接口,也减少了编码和维护的工作量。
预计 Equality<T> 和 Comparison<T> 类能满足多数需求。
如果本文对你有帮助,请推荐本文,让更多的朋友受益。