【问题标题】:Strange behaviour on IEqualityComparerIEqualityComparer 上的奇怪行为
【发布时间】:2013-09-24 17:50:09
【问题描述】:

我正在尝试为我的对象实现一个 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 岁的鲍勃·史密斯 * 14 岁的 Bom Smith * 特雷弗·史密斯 15 岁 * 11 岁的布莱恩·史密斯 * 11 岁的比利·史密斯

但不包括 14 岁的 Bob Smith。 在调试时,我得出的结论是,包含 Bob Smith 的 20 岁开始使用它的比较器有一个比较器,不包括所有 Bob 小于 20 岁的比较器。

在我看来,这是一种奇怪的行为,它应该只排除那些比作者列表中包含的更年轻或年龄相同的行为。 我试图阅读 msdn 文档以及我希望它应该发生的事情: 字体:http://msdn.microsoft.com/en-us/library/bb336390(v=vs.100).aspx

有人可以帮助我吗? 谢谢, 雨果·萨尔加多

【问题讨论】:

  • 您的 Equals 实现对我来说没有多大意义。 >= 取决于列表的先前排序。当比较 20 和 14 时,您会得到不同的结果,具体取决于它们使用的参数 x 或 y。没有机会获得稳定的结果。在 EqualityComparer 中比较是否相等。
  • 你有其他的建议来做我想做的事。如果有更好的方法,我愿意更改我的代码。
  • @HugoSalgado:甚至不清楚你想在这里实现什么。请提供预期的输出并解释它。现在,你直接在源代码下的内容对我来说毫无意义,特别是在任何地方都没有Bom Smith
  • @Ralf 我的目标是得到以下结果: * Bob Smith 20 岁 * Bob Smith 14 岁 * Trevor Smith 15 岁 * Brian Smith 11 岁 * Billy Smith 11 岁
  • @Ralf 我想完成一种方法来检测给定作者是否与 List 中的任何一个具有相同的名称,以及是否是您的或与匹配的那些相同的年龄列表.

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


【解决方案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&lt;T&gt; 用于检查相等性。它与任何排序无关。因此,您不能以您尝试的方式使用它。

通常:此接口的实现应始终在GetHashCode 实现和Equals 方法中使用完全相同的一组属性。

【讨论】:

  • @TimSchmelter:我不关注。但我认为 OP 一开始并没有真正掌握该界面的概念。看起来他想以某种方式同时定义排序和过滤。
  • 谢谢大家的快速回复...所以基本上你说我需要使用所有的名字和姓氏像这样的东西: public bool Equals(Author x, Author y) { if (x.Firstname == y.Firstname && x.Lastname == y.Lastname) 返回 x.Age >= y.Age;返回假;这仍然行不通......我不能使用 GetHash 方法上的 Age,因为我无法比较它。如果您认为这不是最好的方法,请随时给我解决。再次感谢大家雨果
  • 不,这是平等的比较器。你的&gt;= 在这里完全没有意义。
  • 那么有没有其他方法可以检查作者是否具有相同的名字并且比作者更早然后从列表中删除?
  • 谢谢 Daniel... 我知道我不应该使用 except 而是执行一个查询,以得到我想要的结果。与此同时,我找到了一个与你的解决方案有点不同的解决方案。只是好奇你认为哪个性能更好?你或我的解决方案: var result = authorsList .Where(g => !authorsListExcept.Any(ale => ale.Firstname == g.Firstname && ale.Lastname == g.Lastname && ale.Age >= g.Age) ) .ToList();
【解决方案2】:

如果我明白你想要什么,试试这个。

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; }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-12-26
    • 2013-04-20
    • 2021-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多