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

posted @ 2022-08-30 16:35  katesharing  阅读(454)  评论(0编辑  收藏  举报