linq中的Distinct的使用(附带IComparable和IComparer的复习和使用)

Distinct 的使用,说指简单,也比较复杂,因为它拓展的东西比较多

先看本类型的使用:

简单示例:

            List<int> intList = new List<int>() {1,1,2,2,3,3};
            var result=intList.Distinct();

            //能得到我们想要的效果:1 2 3

            List<string> strList = new List<string>() { "1", "1", "2", "2", "3", "3"};
            var resultList = intList.Distinct();

            //能得到我们想要的效果:"1"  "2"  "3"

But 当在我们引用对象的时候,效果就有点不一样了!

 public class Person
    {
        public int ID { get; set; }

        public string Name { get; set; }
    }

    class Program
    {


        static void Main(string[] args)
        {

            List<Person> list = new List<Person>()
            {
                new Person() { ID=1,Name="jack" },
                new Person() { ID=1,Name="jack" },
                new Person() { ID=2,Name="tom" },
                new Person() { ID=2,Name="tom" }
            };
            var result = list.Distinct();
            Console.Read();

        }
    }

结果却是这样的:

 

说明,没有起到我们想要的效果;

原因是,它重复判断定的对象是我们的 引用!

看下面的这个....

 

然后如何得到我们想要的去重复效果呢?

Distinct方法还有另一个重载:

//通过使用指定的 System.Collections.Generic.IEqualityComparer<T> 对值进行比较

//返回序列中的非重复元素。

public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source, 

 IEqualityComparer<TSource> comparer);

 该重载接收一个IEqualityComparer的参数。

假设要按Id来筛选;(需要实现两个方法)

实现代码如下:定义个去除重复的标准)

public class PersonComparer : IEqualityComparer<Person>
    {

        public bool Equals(Person x,Person y)
        {
            if (x == null)
                return y == null;
            return x.ID == y.ID;
        }

        public int GetHashCode(Person obj)
        {
            if (obj == null)
                return 0;
            return obj.ID.GetHashCode();
        }

    }

 

使用方法:

var result = list.Distinct(new PersonComparer());

结果:

 

要是现在我想用name来作为比较的基准呢?

实现代码:

    public class PropertyComparer<T> : IEqualityComparer<T>
    {
        private PropertyInfo _PropertyInfo;  //这个要用到反射

        //写成一个较为通用的方法
        public PropertyComparer(string propertyName)
        {
            _PropertyInfo = typeof(T).GetProperty(propertyName,BindingFlags.GetProperty|BindingFlags.Instance|BindingFlags.Public);
            if (_PropertyInfo == null)
            {
                throw new ArgumentException(string.Format("{0} is not a property of type {1}.",
                propertyName, typeof(T)));
            }
        }

        public bool Equals(T x,T y)
        {
            object xValue = _PropertyInfo.GetValue(x,null);
            object yValue = _PropertyInfo.GetValue(x, null);
            if (xValue == null)
                return yValue == null;
            return xValue.Equals(yValue);
        }

        public int GetHashCode(T obj)
        {
            object propertyValue = _PropertyInfo.GetValue(obj,null);
            if (propertyValue == null)
            {
                return 0;
            }
            else
            {
                return propertyValue.GetHashCode();
            }
        }
    }

 

具体使用:

  var result = list.Distinct(new PropertyComparer<Person>("Name"));

  通过上面的两个示例之后,我们又了一些基本的认识;

 

  写到这里,我们可以看看我们的静态扩展方法的使用滴啊;

 

然后,你以为就完了,太年轻..............

 

去除重复的值,然后留下一种的一个值,你以为只有我们的distinct 可以做到呀;

当然少不了的我们的groupby的使用滴呀;

来自我们Stack Overflow的答案:

 

效果还是挺好得呀

 还有这个方法;我们先把方法贴出来,然后我们再洗洗的品味;

 

 

 

 

 

//在进行比价之前,我们先复习一些基础的东西;IComparable 进行排序的比较; 

先来看一个基本的:

            int a = 12;
            Console.WriteLine(a.CompareTo(16)); //-1
            Console.WriteLine(a.CompareTo(12)); //0
            Console.WriteLine(a.CompareTo(1)); //1

 

以下,我们主要使用:Sort方法要 通过对象去继承IComparable接口来实现排序

  public class Student : IComparable
    {

        public string Name { get; set; }

        public int Age { get; set; }

        //接口中定义的方法;int CompareTo(object obj);; 然后我们来实现自定义的比较低呀

        public int CompareTo(object obj) 
        {
            var s = obj as Student;
            if (Age > s.Age)
            {
                return 1;
            }else if (Age == s.Age)
            {
                return 0;
            }else
            {
                return -1;
            }
           //return Age.CompareTo(s.Age); //可以直接进行比较低呀;

        }

    }
    class Program
    {
        static void Main(string[] args)
        {
           var studentList = new ArrayList();
            studentList.Add(new Student() { Age = 1, Name = "a1" });
            studentList.Add(new Student() { Age = 5, Name = "g1" });
            studentList.Add(new Student() { Age = 4, Name = "b1" });
            studentList.Add(new Student() { Age = 2, Name = "f1" });
            studentList.Sort();
            foreach (var o in studentList)
            {
                Console.WriteLine((o as Student).Age);
            }

            Console.ReadLine();


        }
    }

结果:

 

如果我们不想用用年龄来比较呢;可使用IComparer来实现一个自定义的比较器

  public class SortName : IComparer
    {

        //接口中的定义:int Compare(object x, object y);

        //具体实现;

        public int Compare(object x,object y)
        {
            Student a = x as Student;
            Student b = y as Student;
            return a.Name.CompareTo(b.Name);
        }
    }

 使用方法:studentList.Sort(new SortName()); 

结果:

上面的代码我们使用了一个已经不建议使用的集合类ArrayList,当泛型出来后,所有非泛型集合类已经建议不尽量使用了。

你可以看到上面我们再实现:

IComparable
IComparer
的时候, 频发的进行拆箱和装箱操作滴呀;
所以最好的建议是:
代码中的ArrayList,应该换成List<T>,对应的,我们就该实现IComparable<T>和IComparer<T>。最终的代码应该像:

总结:

比较和排序的概念;

2:IComparable和IComparer;

3:IComparable和IComparer的泛型实现IComparable<T>和IComparer<T>;

1:比较和排序的概念

    比较:两个实体类之间按>,=,<进行比较。

    排序:在集合类中,对集合类中的实体进行排序。排序基于的算法基于实体类提供的比较函数。

    基本型别都提供了默认的比较算法,如string提供了按字母进行比较,int提供了按整数大小进行比较。

于这个 IComparable 接口,因为基本简单的值类型都有 CompareTo() 方法,而且有了 Linq 后,我只要能用 IEnumerable<T> 的集合类型,用 lambda 表达式很容易就能进行排序 Sort() 操作

 其实,对象是一个复合的数据类型;对 对象的比较,我们需要选定一个基准,要是age字段,要么是我们的name字段,或者多个字段组合比较,最终的目的是要我们自己进行自定义类的比较;

我们这里再来看一个示例;

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
   
    //再努力一点,也许运气会好一点;

   public class CarBase : IComparable
    {
        public string modleNo = "";

        public int CompareTo(object obj)
        {
            //定义比较的基准;
            if (obj is CarBase)
            {
                CarBase car = obj as CarBase;
                return this.modleNo.CompareTo(car.modleNo); //比较汽车型号 比较基准就在这里滴呀;
            }else
            {
                throw new Exception("类型不匹配,无法进行比较");
            }

        }


    }

    public class XCAR : CarBase
    {
        public XCAR()
        {
            this.modleNo ="X";
        }
    }

    public class YCAR : CarBase
    {
        public YCAR()
        {
            this.modleNo = "Y";
        }
    }

    public class ZCAR : CarBase
    {
        public ZCAR()
        {
            this.modleNo = "Z";
        }
    }

    //同样,我们可以这样来实现一个东东

    public class CarComparer : IComparer
    {
        public int Compare(object x,object y)
        {
            //返回值 < 0表示: x小于y
            //返回值=0表示:x等于y 
            //返回值>0表示:x大于y 
            if (x is CarBase && y is CarBase)
            {
                //这里涉及类型的转换,性能开销不划算,实际的使用过程中,建议使用泛型;
                CarBase car1 = x as CarBase;
                CarBase car2 = y as CarBase;
                return car1.CompareTo(car2);
            }
            else
            throw new Exception("类型不匹配");
        }
    }



    class Program
    {
        static void Main(string[] args)
        {
            CarBase x = new XCAR();
            CarBase y = new YCAR();
            CarBase z = new ZCAR();
            int resutl = x.CompareTo(z);  //x<y  所以我们的结果就是:-1  两个对象之间的各种比较滴呀,效果还是满理想得啊;
            Console.WriteLine(resutl);
            object[] cars = new object[] { y,x, z };//汽车数组,用于排序,现在是无序状态 

            foreach(var o in cars)
            {
                Console.WriteLine((o as CarBase).modleNo);
            }

            // y x z

            //然后进行排序;
            Array.Sort(cars,new CarComparer());
            //排序呢后的结果;
            foreach (var o in cars)
            {
                Console.WriteLine((o as CarBase).modleNo);
            }

            // x y  z
            Console.ReadLine();
        }
    }
}

 

 上面的方法虽然好,但是如果你想想,如果有一百个对象都要进行比较,那么,我么是不是就要写一百个对象继承自我们的BaseCar的方法呢;

如果有更好的优化方法呢;

 

 

 

参看文献:

http://www.csframework.com/archive/2/arc-2-20110713-1710.htm

posted @ 2017-08-01 21:06  咕-咚  阅读(430)  评论(0编辑  收藏  举报