Linq:Distinct()不能排除重复对象的解决方案
1.数据准备:
假设有几个重复数据,如下,(正常使用Distinct()方法,我们想要排除掉重复的对象)
using System.Collections.Generic; namespace LINQTutorial { public class Student { public int ID { get; set; } public string Name { get; set; } public string Gender { get; set; } public string Branch { get; set; } public int Age { get; set; } public static List<Student> GetAllStudents() { return new List<Student>() { new Student { ID = 1001, Name = "玲玲", Gender = "Female", Branch = "CSE", Age = 20 }, new Student { ID = 1002, Name = "张三", Gender = "Male", Branch = "ETC", Age = 21 }, new Student { ID = 1003, Name = "李四", Gender = "Male", Branch = "CSE", Age = 21 }, new Student { ID = 1004, Name = "王五", Gender = "Male", Branch = "CSE", Age = 20 }, new Student { ID = 1005, Name = "珊珊", Gender = "Female", Branch = "ETC", Age = 20 }, new Student { ID = 1006, Name = "涵涵", Gender = "Female", Branch = "CSE", Age = 21 }, new Student { ID = 1007, Name = "钱六", Gender = "Male", Branch = "CSE", Age = 22 }, new Student { ID = 1008, Name = "婷婷", Gender = "Female", Branch = "CSE", Age = 20 }, new Student { ID = 1009, Name = "兰兰", Gender = "Female", Branch = "ETC", Age = 22 }, new Student { ID = 1010, Name = "黄九", Gender = "Male", Branch = "ETC", Age = 21 }, new Student { ID = 1001, Name = "玲玲", Gender = "Female", Branch = "CSE", Age = 20 }, new Student { ID = 1002, Name = "张三", Gender = "Male", Branch = "ETC", Age = 21 }, new Student { ID = 1003, Name = "李四", Gender = "Male", Branch = "CSE", Age = 21 }, new Student { ID = 1004, Name = "王五", Gender = "Male", Branch = "CSE", Age = 20 } }; } } }
2.使用Distinct()方法
static void DistinctExp() { //Using Method Syntax var MS = Student.GetAllStudents() .Distinct().ToList(); //Using Query Syntax var QS = (from std in Student.GetAllStudents() select std) .Distinct().ToList(); foreach (var item in QS) { Console.WriteLine($"ID : {item.ID} , Name : {item.Name} "); } }
3.测试结果如下
4.发现问题
为啥这边没有过滤掉重复的列?
原因是:用于比较的默认比较器只检查两个对象引用是否相等,而不检查复杂对象的单个属性值。
5.解决问题
1)方案一:实现IEqualityComparer接口
using System.Collections.Generic; namespace LINQTutorial { public class StudentComparer : IEqualityComparer<Student> { public bool Equals(Student x, Student y) { //首先检查两个对象引用是否相等,相等返回true if (object.ReferenceEquals(x, y)) { return true; } //如果任何一个对象的引用为null,返回false if (object.ReferenceEquals(x, null) || object.ReferenceEquals(y, null)) { return false; } //比较每个对象的每一个属性 return x.ID == y.ID && x.Name == y.Name && x.Gender == y.Gender && x.Branch == y.Branch && x.Age == y.Age; } public int GetHashCode(Student obj) { //如果对象为空, if (obj == null) { return 0; } //获取Hash code值 int IDHashCode = obj.ID.GetHashCode(); int NameHashCode = obj.Name == null ? 0 : obj.Name.GetHashCode(); int GenderHashCode = obj.Gender == null ? 0 : obj.Gender.GetHashCode(); int BranchHashCode = obj.Branch == null ? 0 : obj.Branch.GetHashCode(); int AgeHashCode = obj.Age.GetHashCode(); return IDHashCode ^ NameHashCode ^ GenderHashCode ^ BranchHashCode ^ AgeHashCode;//这么写对么? } } }
改变主方法,如下
static void DistinctSolution1() { StudentComparer studentComparer = new StudentComparer(); //Using Method Syntax var MS = Student.GetAllStudents() .Distinct(studentComparer).ToList(); //Using Query Syntax var QS = (from std in Student.GetAllStudents() select std) .Distinct(studentComparer).ToList(); foreach (var item in QS) { Console.WriteLine($"ID : {item.ID} , Name : {item.Name} "); } Console.ReadKey(); }
测试结果如下,符合预期:
Distinct方法官方参考资料如下:
2)方案二:重写Student类中Equals()方法和GetHashCode()方法
主方法还保持跟原来一样,如下:
3)方案三:使用匿名类型
4)方案四:使用IEquatable接口
public class Student : IEquatable<Student> { public int ID { get; set; } public string Name { get; set; } public string Gender { get; set; } public string Branch { get; set; } public int Age { get; set; } public static List<Student> GetAllStudents() { return new List<Student>() { new Student { ID = 1001, Name = "玲玲", Gender = "Female", Branch = "CSE", Age = 20 }, new Student { ID = 1002, Name = "张三", Gender = "Male", Branch = "ETC", Age = 21 }, new Student { ID = 1003, Name = "李四", Gender = "Male", Branch = "CSE", Age = 21 }, new Student { ID = 1004, Name = "王五", Gender = "Male", Branch = "CSE", Age = 20 }, new Student { ID = 1005, Name = "珊珊", Gender = "Female", Branch = "ETC", Age = 20 }, new Student { ID = 1006, Name = "涵涵", Gender = "Female", Branch = "CSE", Age = 21 }, new Student { ID = 1007, Name = "钱六", Gender = "Male", Branch = "CSE", Age = 22 }, new Student { ID = 1008, Name = "婷婷", Gender = "Female", Branch = "CSE", Age = 20 }, new Student { ID = 1009, Name = "兰兰", Gender = "Female", Branch = "ETC", Age = 22 }, new Student { ID = 1010, Name = "黄九", Gender = "Male", Branch = "ETC", Age = 21 }, new Student { ID = 1001, Name = "玲玲", Gender = "Female", Branch = "CSE", Age = 20 }, new Student { ID = 1002, Name = "张三", Gender = "Male", Branch = "ETC", Age = 21 }, new Student { ID = 1003, Name = "李四", Gender = "Male", Branch = "CSE", Age = 21 }, new Student { ID = 1004, Name = "王五", Gender = "Male", Branch = "CSE", Age = 20 } }; } public bool Equals(Student other) { if (object.ReferenceEquals(other, null)) { return false; } if (object.ReferenceEquals(this, other)) { return true; } return this.ID == other.ID && this.Name == other.Name && this.Gender == other.Gender && this.Branch == other.Branch && this.Age == other.Age; } public override int GetHashCode() { //获取Hash code值 int IDHashCode = this.ID.GetHashCode(); int NameHashCode = this.Name == null ? 0 : this.Name.GetHashCode(); int GenderHashCode = this.Gender == null ? 0 : this.Gender.GetHashCode(); int BranchHashCode = this.Branch == null ? 0 : this.Branch.GetHashCode(); int AgeHashCode = this.Age.GetHashCode(); return IDHashCode ^ NameHashCode ^ GenderHashCode ^ BranchHashCode ^ AgeHashCode;//这么写对么? } }
主方法还保持跟原来一样,如下:
上面4种解决方案的测试结果都如下:
延伸:
IEqualityComparer<T> 和IEquatable<T>的区别:
IEqualityComparer<T>是一个对象的接口,该对象在两个T类型的对象上执行比较,而IEquatable<T>也是一个T类型的对象的接口,它可以将自己与另一个对象进行比较。
参考网址:
https://dotnettutorials.net/lesson/linq-distinct-method/
代码在:CSharpBasic\LINQTutorial
本文来自博客园,转载请注明原文链接:https://www.cnblogs.com/keeplearningandsharing/p/16639903.html