List.Contains <t>总是给出错误的</t>

时间:2011-10-07 06:45:51

标签: c# list hash override equals

似乎许多人已经遇到过这个问题:

List not working as expected

Contains always giving false

所以我看到了答案,并试图实现Equals和GetHashCode的重写,但似乎我编写了错误的代码。

这种情况:我有一个Users(Class)列表,每个用户都有一个List和一个Name属性,list属性包含许可证。我正在尝试做一个

         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的代码,我试图查看列表是否已包含某个用户,如果没有,则添加不存在的用户。包含仅在第一次工作时,当currentUser更改时,列表中的用户更改为当前用户可能这是一个与equals,any ideas无关的问题?

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

5 个答案:

答案 0 :(得分:2)

好吧,您的哈希码有一个问题 - 如果 没有许可证用户名为空,则忽略其他组件。我把它重写为:

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<T>无论如何都不会使用GetHashCode。 (你应该绝对适当地覆盖它,但它不会是错误的原因。)

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

答案 1 :(得分:1)

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

答案 2 :(得分:0)

您需要为License类实现Equals和GetHashcode,否则SequenceEqual将无效。

答案 3 :(得分:0)

您的班级是否实施IEquatable<User>?从你的平等方法看来它只是检查。

List.Contains的文档指出:

  

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

答案 4 :(得分:0)

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

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

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

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

如果_userName字段没有改变,那么只需返回其哈希码,看看它是否有效。