IEqualityComparer上的奇怪行为

时间:2013-09-19 14:58:29

标签: c# .net linq-to-objects iequalitycomparer except

我正在尝试为我的对象实现IEqualityComparer,它基本上检测对象是否比另一个更旧。以下更简单的例子将综合我正在努力实现的目标:

class Program
{
    static void Main(string[] args)
    {
        var authorsList = new List<Author>()
        {
            new Author{ Firstname = "Bob", Lastname = "Smith", Age=11 },
            new Author{ Firstname = "Bob", Lastname = "Smith", Age=20 },
            new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
            new Author{ Firstname = "Bob", Lastname = "Smith", Age=14 },
            new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
            new Author{ Firstname = "Fred", Lastname = "Smith", Age=12 },
            new Author{ Firstname = "Trevor", Lastname = "Smith", Age=15 },
            new Author{ Firstname = "Brian", Lastname = "Smith", Age=11 },
            new Author{ Firstname = "Billy", Lastname = "Smith", Age=11 },
        };
        var authorsListExcept = new List<Author>()
        {
            new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
            new Author{ Firstname = "Fred", Lastname = "Smith", Age=12 },
        };
        var authorsList2 = authorsList
            .Except(authorsListExcept, new AuthorUpdatedComparer()).ToList();
    }
}

class Author
{
    public string Firstname { get; set; }
    public string Lastname { get; set; }
    public int Age { get; set; }
}

class AuthorUpdatedComparer : IEqualityComparer<Author>
{
    public bool Equals(Author x, Author y)
    {
        return x.Age >= y.Age;
    }

    public int GetHashCode(Author obj)
    {
        //Check whether the object is null 
        if (Object.ReferenceEquals(obj, null)) return 0;

        int FirstnameHash = (obj.Firstname ?? "").GetHashCode();
        int LastnameHash = (obj.Lastname ?? "").GetHashCode();
        int finalResult = FirstnameHash ^ LastnameHash;

        return finalResult;
    }
}

我的authorsList2结果将是: *鲍勃史密斯20岁 * Bom Smith,14岁 * Trevor Smith,15岁 * Brian Smith,11岁 *比利史密斯11岁

但不包括这个,不包括14岁的鲍勃史密斯。 在调试时,我得出的结论是,包括Bob Smith在20岁开始使用它之后的Comparer有一个比较器,在所有Bob小于20之后排除。

在我看来,这是一种奇怪的行为,它应该只排除那些年龄较小或年龄与authorsListExcept所包含的年龄相同的行为。 我试着阅读msdn文档,我想要它应该发生: 字体:http://msdn.microsoft.com/en-us/library/bb336390(v=vs.100).aspx

任何人都可以帮助我吗? 谢谢, 雨果萨尔加多

2 个答案:

答案 0 :(得分:1)

以下LINQ查询提供了您期望的结果:

var result = 
    authorsList.GroupBy(x => Tuple.Create(x.Firstname, x.Lastname))
               .SelectMany(g => g.Where(x => authorsListExcept.All(e => e.Firstname != x.Firstname || e.Lastname != x.Lastname || e.Age < x.Age)));

以下查询也会生成此结果。它应该表现得更好:

var result = 
    authorsList.GroupBy(x => Tuple.Create(x.Firstname, x.Lastname))
               .GroupJoin(authorsListExcept, x => x.Key,
                          x => Tuple.Create(x.Firstname, x.Lastname),
                          (a, e) => a.Where(x => x.Age > e.Select(z => z.Age)
                                                          .DefaultIfEmpty(0)
                                                          .Max()))
               .SelectMany(x => x)

第三个选项(与之前相同,但在查询语法中):

var result = 
    (from a in authorsList
    group a by Tuple.Create(a.Firstname, a.Lastname) into g
    join e in authorsListExcept on g.Key equals Tuple.Create(e.Firstname, e.Lastname) into er
    from age in er.Select(x => x.Age).DefaultIfEmpty()
    select g.Where(x => x.Age > age)).SelectMany(x => x);

接口IEqualityComparer<T>用于检查相等。它与任何订购无关。因此,您不能以您尝试的方式使用它。

一般而言:此接口的实现应始终在GetHashCode实现和Equals方法中使用完全相同的属性集。

答案 1 :(得分:0)

如果我理解了你想要的东西,试试这个。

class Program
{
    static void Main(string[] args)
    {
        var authorsList = new List<Author>()
        {
            new Author{ Firstname = "Bob", Lastname = "Smith", Age=11 },
            new Author{ Firstname = "Bob", Lastname = "Smith", Age=20 },
            new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
            new Author{ Firstname = "Bob", Lastname = "Smith", Age=14 },
            new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
            new Author{ Firstname = "Fred", Lastname = "Smith", Age=12 },
            new Author{ Firstname = "Trevor", Lastname = "Smith", Age=15 },
            new Author{ Firstname = "Brian", Lastname = "Smith", Age=11 },
            new Author{ Firstname = "Billy", Lastname = "Smith", Age=11 },
        };
        var authorsListExcept = new List<Author>()
        {
            new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
            new Author{ Firstname = "Fred", Lastname = "Smith", Age=12 },
        };

        var authorsList2 = authorsList.Where(x => !authorsListExcept.Any(y => y.Firstname == x.Firstname && y.Lastname == x.Lastname && x.Age <= y.Age));
    }
}

public class Author
{
    public string Firstname { get; set; }
    public string Lastname { get; set; }
    public int Age { get; set; }
}