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