雁过请留痕...
代码改变世界

通用的泛型Icomparer生成类,对类进行排序,最大支持4个字段同时比较

2012-06-07 15:18  xiashengwang  阅读(340)  评论(0编辑  收藏  举报

一,通常我们自己定义一个类,然后对类的集合进行排序,是下面这样的做法

        class People
        {
            public int ID { get; set; }
            public string Name { get; set; }
            public DateTime Birthday { get; set; }

            public override string ToString()
            {
                return string.Format("{0},{1},{2}", ID, Name, Birthday);
            }
        }

 再定义一个类继承IComparer<People>接口

        /// <summary>
        /// user define
        /// </summary>
        class PeopleCompare : IComparer<People>
        {
            #region IComparer<People> 

            public int Compare(People x, People y)
            {
                return Comparer<int>.Default.Compare(x.ID, y.ID);
            }

            #endregion
        }

 调用代码:

        public static void TestFunc()
        {
            var peoples = new People[] { 
            new People{ID = 1,Name = "a123",Birthday = new DateTime(2000,05,01)},
            new People{ID = 4,Name = "b123",Birthday = new DateTime(2000,04,01)},
            new People{ID = 2,Name = "d123",Birthday = new DateTime(2000,02,01)},
            new People{ID = 3,Name = "e123",Birthday = new DateTime(2000,04,01)},
            };
            Array.Sort(peoples, new PeopleCompare());            
            peoples.ForEach<People>(p => System.Diagnostics.Debug.WriteLine(p));

        }

 结果输出如下:

1,a123,2000/05/01 0:00:00
2,d123,2000/02/01 0:00:00
3,e123,2000/04/01 0:00:00
4,b123,2000/04/01 0:00:00

,如果我们又定义了Cat,Monkey等等这样的类,就得每次都写一个类似于PeopleCompare的类,当然你也可以在定义People的时候继承Icomparable接口,本文不讨论。

这样做有大量的重复工作,现在考虑定义一个泛型的方法来创建通用的继承了IComparer的类,并且考虑到类有多个字段,对多个字段的排序也考虑进去。下面的这个类定义了一个最大支持4个字段的ComparerFactory类,一般应用已经足够了。

        /// <summary>
        /// Common Comaparer Generate Class, supported 4 paramers limitted
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public static class ComparerFactroy<T>
        {
            public static IComparer<T> Create<V1>(Func<T, V1> keySelector1)
            {
                return new MyCommonComparer<V1, Object, Object, Object>(keySelector1,Comparer<V1>.Default,null,null,null, null, null, null);
            }
            public static IComparer<T> Create<V1, V2>(Func<T, V1> keySelector1, Func<T, V2> keySelector2)
            {
                return new MyCommonComparer<V1, V2, Object, Object>(keySelector1,Comparer<V1>.Default, keySelector2,Comparer<V2>.Default,null,null, null, null);
            }
            public static IComparer<T> Create<V1, V2, V3>(Func<T, V1> keySelector1, Func<T, V2> keySelector2, Func<T, V3> keySelector3)
            {
                return new MyCommonComparer<V1, V2, V3, Object>(keySelector1, Comparer<V1>.Default, keySelector2, Comparer<V2>.Default, keySelector3, Comparer<V3>.Default, null,null);
            }
            public static IComparer<T> Create<V1, V2, V3,V4>(Func<T, V1> keySelector1, Func<T, V2> keySelector2, Func<T, V3> keySelector3, Func<T, V4> keySelector4)
            {
                return new MyCommonComparer<V1, V2, V3, V4>(keySelector1, Comparer<V1>.Default, keySelector2, Comparer<V2>.Default, keySelector3, Comparer<V3>.Default, keySelector4,Comparer<V4>.Default);
            }

            class MyCommonComparer<V1,V2,V3,V4> : IComparer<T>
            {
                Func<T, V1> keySelector1;
                Func<T, V2> keySelector2;
                Func<T, V3> keySelector3;
                Func<T, V4> keySelector4;

                IComparer<V1> comparer1;
                IComparer<V2> comparer2;
                IComparer<V3> comparer3;
                IComparer<V4> comparer4;

                public MyCommonComparer(Func<T, V1> keySelector1, IComparer<V1> compare1,
                                        Func<T, V2> keySelector2, IComparer<V2> compare2,
                                        Func<T, V3> keySelector3, IComparer<V3> compare3,
                                        Func<T, V4> keySelector4, IComparer<V4> compare4)
                {
                    this.keySelector1 = keySelector1;
                    this.keySelector2 = keySelector2;
                    this.keySelector3 = keySelector3;
                    this.keySelector4 = keySelector4;
                    this.comparer1 = compare1;
                    this.comparer2 = compare2;
                    this.comparer3 = compare3;
                    this.comparer4 = compare4;
                }
                #region IComparer<T> メンバ

                public int Compare(T x, T y)
                {
                    int retVal = 0;
                    if (keySelector1 != null)
                        retVal = comparer1.Compare(keySelector1(x), keySelector1(y));
                    if (keySelector2 != null && retVal == 0)
                        retVal = comparer2.Compare(keySelector2(x), keySelector2(y));
                    if (keySelector3 != null && retVal == 0)
                        retVal = comparer3.Compare(keySelector3(x), keySelector3(y));
                    if (keySelector4 != null && retVal == 0)
                        retVal = comparer4.Compare(keySelector4(x), keySelector4(y));

                    return retVal;
                }

                #endregion
            }
        }

 ComparerFactory的Create方法根据你传的类型,创建一个Comparer,Create方法有4个重载,可以对1,2,3,4个字段进行排序。

继续用上面定义的People类测试,使用方法如下:

        public static void TestFunc()
        {
            var peoples = new People[] { 
                new People{ID = 1,Name = "a123",Birthday = new DateTime(2000,05,01)},
                new People{ID = 4,Name = "b123",Birthday = new DateTime(2000,04,01)},
                new People{ID = 2,Name = "d123",Birthday = new DateTime(2000,02,01)},
                new People{ID = 3,Name = "e123",Birthday = new DateTime(2000,04,01)},
                new People{ID = 3,Name = "e123",Birthday = new DateTime(2000,03,01)},
                new People{ID = 3,Name = "c123",Birthday = new DateTime(2000,03,01)}
                };

            Array.Sort(peoples, ComparerFactroy<People>.Create(p => p.ID, p => p.Name, p => p.Birthday));

            peoples.ForEach<People>(p => System.Diagnostics.Debug.WriteLine(p));

        }

 分别根据People的ID,Name,Birthday进行排序

测试结果:

1,a123,2000/05/01 0:00:00
2,d123,2000/02/01 0:00:00
3,c123,2000/03/01 0:00:00
3,e123,2000/03/01 0:00:00
3,e123,2000/04/01 0:00:00
4,b123,2000/04/01 0:00:00

 
可以继续完善的地方:

1,现在默认是每个字段的升序排列,可以在参数中每个字段的升序降序排列。

2,现在只支持4个字段,扩充字段代码改动较大,不知道还有没有更好的支持多字段的写法,欢迎一起讨论。