代码之家  ›  专栏  ›  技术社区  ›  Simsons

具有IEquatable的LINQ DISTINCT未返回DISTINCT结果[重复]

  •  0
  • Simsons  · 技术社区  · 6 年前
    class Program
    {
        static void Main(string[] args)
        {
            List<Book> books = new List<Book> 
            {
                new Book
                {
                    Name="C# in Depth",
                    Authors = new List<Author>
                    {
                        new Author 
                        {
                            FirstName = "Jon", LastName="Skeet"
                        },
                         new Author 
                        {
                            FirstName = "Jon", LastName="Skeet"
                        },                       
                    }
                },
                new Book
                {
                    Name="LINQ in Action",
                    Authors = new List<Author>
                    {
                        new Author 
                        {
                            FirstName = "Fabrice", LastName="Marguerie"
                        },
                         new Author 
                        {
                            FirstName = "Steve", LastName="Eichert"
                        },
                         new Author 
                        {
                            FirstName = "Jim", LastName="Wooley"
                        },
                    }
                },
            };
    
    
            var temp = books.SelectMany(book => book.Authors).Distinct();
            foreach (var author in temp)
            {
                Console.WriteLine(author.FirstName + " " + author.LastName);
            }
    
            Console.Read();
        }
    
    }
    public class Book
    {
        public string Name { get; set; }
        public List<Author> Authors { get; set; }
    }
    public class Author
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public override bool Equals(object obj)
        {
            return true;
            //if (obj.GetType() != typeof(Author)) return false;
            //else return ((Author)obj).FirstName == this.FirstName && ((Author)obj).FirstName == this.LastName;
        }
    
    }
    

    这是基于“linq in action”中的一个例子。清单4.16。

    这印了乔恩·斯凯特两次。为什么?我甚至尝试过在author类中重写equals方法。仍然清晰的似乎不起作用。我错过了什么?

    编辑: 我已经加了==和!=操作员过载。仍然没有帮助。

     public static bool operator ==(Author a, Author b)
        {
            return true;
        }
        public static bool operator !=(Author a, Author b)
        {
            return false;
        }
    
    0 回复  |  直到 8 年前
        1
  •  145
  •   Matt    7 年前

    当涉及到自定义对象时,linq distinct并没有那么聪明。

    它所做的只是查看您的列表,并看到它有两个不同的对象(它不关心它们对于成员字段有相同的值)。

    一种解决方法是实现如图所示的iequatable接口。 here 是的。

    如果您像这样修改author类,它应该可以工作。

    public class Author : IEquatable<Author>
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    
        public bool Equals(Author other)
        {
            if (FirstName == other.FirstName && LastName == other.LastName)
                return true;
    
            return false;
        }
    
        public override int GetHashCode()
        {
            int hashFirstName = FirstName == null ? 0 : FirstName.GetHashCode();
            int hashLastName = LastName == null ? 0 : LastName.GetHashCode();
    
            return hashFirstName ^ hashLastName;
        }
    }
    

    Try it as DotNetFiddle

        2
  •  65
  •   Rex M    15 年前

    这个 Distinct() 方法检查引用类型的引用相等性。这意味着它要寻找完全相同的重复对象,而不是包含相同值的不同对象。

    有一个 overload 这需要一个 IEqualityComparer ,因此可以指定不同的逻辑来确定给定对象是否等于另一个对象。

    如果希望author的行为正常,就像一个普通的对象(即只引用相等),但是为了按名称值进行不同的检查相等,请使用 IEQualityComparer公司 是的。如果始终希望根据名称值比较author对象,则 重写gethashcode并等于 ,或 实现iEquatable 是的。

    上的两个成员 IEqualityComparer 接口是 Equals GetHashCode 是的。你判断两个 Author 如果名字和姓氏字符串相同,则对象似乎相等。

    public class AuthorEquals : IEqualityComparer<Author>
    {
        public bool Equals(Author left, Author right)
        {
            if((object)left == null && (object)right == null)
            {
                return true;
            }
            if((object)left == null || (object)right == null)
            {
                return false;
            }
            return left.FirstName == right.FirstName && left.LastName == right.LastName;
        }
    
        public int GetHashCode(Author author)
        {
            return (author.FirstName + author.LastName).GetHashCode();
        }
    }
    
        3
  •  42
  •   Jehof    12 年前

    另一个没有实现的解决方案 IEquatable 我是说, Equals GetHashCode 是使用LINQ GroupBy 方法并从igroup中选择第一个项。

    var temp = books.SelectMany(book => book.Authors)
                    .GroupBy (y => y.FirstName + y.LastName )
                    .Select (y => y.First ());
    
    foreach (var author in temp){
      Console.WriteLine(author.FirstName + " " + author.LastName);
    }
    
        4
  •  25
  •   Makis Mak Ashu_90    6 年前

    还有一种方法可以从用户定义的数据类型列表中获取不同的值:

    YourList.GroupBy(i => i.Id).Select(i => i.FirstOrDefault()).ToList();
    

    当然,它会给出不同的数据集

        5
  •  20
  •   AndyM    15 年前

    Distinct() 对可枚举中的对象执行默认相等比较。如果你还没有推翻 Equals() GetHashCode() ,然后在 object ,它比较引用。

    简单的解决方案是添加 对的 实施 等于() GetHashcode() 与您正在比较的对象图中的所有类(即book和author)进行比较。

    这个 IEqualityComparer 接口是一种方便,允许您实现 等于() GetHashcode() 在一个单独的类中,当您无法访问需要比较的类的内部时,或者如果您使用不同的比较方法时。

        6
  •  10
  •   Eric King    15 年前

    您已经重写了equals(),但请确保也重写了gethashcode()

        7
  •  7
  •   Alex    9 年前

    以上答案是错误的!!! 在msdn上声明的distinct返回默认赤道 默认属性检查类型T是否实现System.iequatable接口,如果实现,则返回使用该实现的EqualityComparer。 否则,它返回一个equalityComparer,它使用t提供的object.equals和object.getHashcode的重写

    也就是说只要你过了头就没事了。

    您的代码不起作用的原因是您选中了firstname==lastname。

    看见 https://msdn.microsoft.com/library/bb348436(v=vs.100).aspx https://msdn.microsoft.com/en-us/library/ms224763(v=vs.100).aspx