【问题标题】:List.Contains<T> always giving falseList.Contains<T> 总是给出 false
【发布时间】:2011-10-07 06:45:51
【问题描述】:

这个问题好像很多人都遇到过:

List not working as expected

Contains always giving false

所以我看到了答案并尝试实现对 Equals 和 GetHashCode 的覆盖,但似乎我的编码有问题。

情况是这样的:我有一个用户(类)列表,每个用户都有一个列表和一个名称属性,列表属性包含许可证。我正在尝试做一个

         if (!users.Contains(currentUser))

但它没有按预期工作。这是我为覆盖 Equals 和 GetHashCode 所做的代码:

    public override bool Equals(object obj)
    {
        return Equals(obj as User);
    }


    public bool Equals(User otherUser)
    {
        if (ReferenceEquals(otherUser, null))
            return false;

        if (ReferenceEquals(this, otherUser))
            return true;

        return this._userName.Equals(otherUser.UserName) && 
               this._licenses.SequenceEqual<string>(otherUser.Licenses);
    }

    public override int GetHashCode()
    {
        int hash = 13;
        if (!_licenses.Any() && !_userName.Equals(""))
        {
            unchecked
            {
                foreach (string str in Licenses)
                {
                    hash *= 7;
                    if (str != null) hash = hash + str.GetHashCode();
                }
                hash = (hash * 7) + _userName.GetHashCode();
            }
        }
        return hash;
    }

提前感谢您的建议和帮助!

编辑 1:

这是我正在执行 List.Contains 的代码,我试图查看列表是否已经包含某个用户,如果没有,则添加不存在的用户。 Contains 仅在第一次工作,当 currentUser 更改时,列表中的 User 更改为当前用户,这可能是与 equals 无关的问题,有什么想法吗?

        if (isIn)
        {
            if (!listOfLicenses.Contains(items[3]))
                listOfLicenses.Add(items[3]);

            if (!users.Contains(currentUser))
            {
                User user2Add = new User();
                user2Add.UserName = currentUser.UserName;
                users.Add(user2Add);
                userIndexer++;
            }

            if (users[userIndexer - 1].UserName.Equals(currentUser.UserName))
            {
                users[userIndexer - 1].Licenses.Add(items[3]);
            }
            result.Rows.Add();
        }

【问题讨论】:

  • 许可证列表有变化吗? _userName 有变化吗?
  • 您是否在调试器中单步执行以查看哪一行产生了不正确的结果?
  • 我会删除if (!_licenses.Any() &amp;&amp; !_userName.Equals("")),因为它非常没用,但除此之外,它似乎还可以。您确定您的Licenses 顺序相同吗?
  • 什么意思?我浏览了一个日志文件,并且在 StreamReading 文件时 currentUser 不断变化。由于某种原因,包含只工作一次(第一次),然后当 currentUser 更改用户列表的内部时也会更改。我想这不是很清楚,所以我将在编辑中发布相应的代码。
  • @L.B 我不明白你的意思你能进一步解释一下吗? :-)

标签: c# list hash overriding equals


【解决方案1】:

嗯,你的哈希码有一个问题 - 如果 either 没有许可证 用户名是空的,那么你忽略了另一个组件。我将其重写为:

public override int GetHashCode()
{
    unchecked
    {
        int hash = 17;
        hash = hash * 31 + _userName.GetHashCode();
        foreach (string licence in Licences)
        {
            hash = hash * 31 + licences.GetHashCode();
        }
        return hash;
    }
}

更短更简单。是使用空字符串的哈希码,还是遍历空集合都没有关系。

也就是说,我本来希望之前的代码能够正常工作。请注意,它对许可证订单敏感...哦,List&lt;T&gt; 无论如何都不会使用GetHashCode。 (您绝对应该适当地覆盖它,但这不会是错误的原因。)

如果你能展示一个简短但完整的程序来演示这个问题,那真的很有帮助 - 我强烈怀疑你会发现它实际上是你的测试数据的问题。

【讨论】:

  • 非常好的建议,我会尝试在新项目中单独制作,并会及时通知您。
  • 我无法制作一个简短但完整的程序,因为这可能涉及再次制作几乎相同的项目。我认为这与它对订单敏感有关,你知道我怎样才能让它对订单不敏感吗? :)
  • @Joze:为什么要再次创建同一个项目?它只需要 User 类和一个小型控制台应用程序来创建您认为应该相等但不相等的两个用户。至于订单敏感性——如果这些是许可证,那么我建议将它们保存在HashSet&lt;string&gt;中——然后你可以使用SetEquals方法。
  • 好的,问题似乎已经解决了,但我不确定是许可证订单。无论如何,非常感谢您的帮助和时间!
  • @Joze:您应该真正决定如何考虑许可证的收集。一些选项:总是排序;任意顺序但很重要;任意不重要的顺序(一组)。然后,您可以根据需要使用正确的集合。 (您还应该考虑是否要允许重复。)
【解决方案2】:

users[userIndexer - 1].Licenses.Add(items[3]) 之后,users[userIndexer - 1] 不再是同一个用户。您已更改用于相等比较的许可证 (in User.Equals)。

--编辑 见下面代码

public class Class
{
    static void Main(string[] args)
    {
        User u1 = new User("1");
        User u2 = new User("1");
        Console.WriteLine(u1.Equals(u2));
        u2.Lic = "2";
        Console.WriteLine(u1.Equals(u2));
    }
}

public class User
{
    public string Lic;        
    public User(string lic)
    {
        this.Lic = lic;
    }

    public override bool Equals(object obj)
    {
        return (obj as User).Lic == Lic;
    }
}

【讨论】:

  • 哦,我明白了,谢谢,我会检查一下是否影响它。
  • 问题是,只有在用户名相同的情况下,我才添加许可证,这对我来说是有意义的。用户是否不同(许可证不同)并不重要,重要的是他们的用户名相同,以便可以添加新许可证而不是全新用户。你有什么建议可以解决这个问题吗?
  • 只是比较Equals函数中的名字?
【解决方案3】:

您需要为License 类实现 Equals 和 GetHashcode,否则 SequenceEqual 将不起作用。

【讨论】:

  • 没有许可证类别。许可证是一个通用字符串。如果不能使用 SequenceEqual,我可以使用什么?
  • 哦,那它应该可以正常工作,我唯一能想到的就是许可证的顺序是否错误。我推荐调试器。
【解决方案4】:

您的班级是否实现了IEquatable&lt;User&gt;?从您的相等方法看来,它确实可以,但只是检查。

List.Contains 的文档指出:

此方法通过使用默认相等来确定相等 比较器,由对象的实现定义 IEquatable(T).T 的Equals 方法(列表中值的类型)

【讨论】:

  • 是的,我愿意。我在 User 类中完成所有操作。 User : IEquatable 好点虽然忘了提。
【解决方案5】:

确保GetHashCode 返回的值永远不会因对象的特定实例而改变,这一点非常重要。如果值更改,则列表和字典将无法正常工作。

GetHashCode 视为“GetPrimaryKey”。如果有人向用户添加了新许可证,您将不会更改数据库中用户记录的主键。同样,您不得更改GetHashCode

从您的代码中可以看出,您正在更改许可证集合,并且您正在使用它来计算您的哈希码。所以这可能是导致您的问题的原因。

现在,为您生成的每个哈希码使用一个常量值是完全合法的 - 例如,您可以为每个实例返回 42。这将强制调用Equals 来确定两个对象是否相等。拥有不同的哈希码所做的只是缩短调用 Equals 的需要。

如果_userName 字段没有更改,则只需返回其哈希码并查看它是否有效。

【讨论】:

    猜你喜欢
    • 2015-08-13
    • 1970-01-01
    • 1970-01-01
    • 2013-09-14
    • 1970-01-01
    • 2014-04-24
    • 1970-01-01
    • 2016-12-06
    • 2016-12-05
    相关资源
    最近更新 更多