C# 列表。包含<;T>;总是说假话
似乎不少人已经遇到过这个问题: 因此,我看到了答案,并试图实现Equals和GetHashCode的覆盖,但似乎我在编写错误的代码 情况是这样的:我有一个用户列表(类),每个用户都有一个列表和一个名称属性,列表属性包含许可证。我正在试着做一件事C# 列表。包含<;T>;总是说假话,c#,list,hash,overriding,equals,C#,List,Hash,Overriding,Equals,似乎不少人已经遇到过这个问题: 因此,我看到了答案,并试图实现Equals和GetHashCode的覆盖,但似乎我在编写错误的代码 情况是这样的:我有一个用户列表(类),每个用户都有一个列表和一个名称属性,列表属性包含许可证。我正在试着做一件事 if (!users.Contains(currentUser)) 但它并没有像预期的那样发挥作用。这是我用来覆盖Equals和GetHashCode的代码: public override bool Equals(obj
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;
}
您需要为
许可证
类实现Equals和GetHashcode,否则SequenceEqual将不起作用。您的类是否实现了IEquatable
?从您的相等方法来看,它似乎只进行检查
列表的文档包含以下内容:
此方法通过使用默认相等来确定相等
比较器,由对象的
IEquatable(T).Equals方法用于T(列表中的值类型)
好吧,你的哈希代码有一个问题——如果没有许可证或者用户名为空,你就会忽略另一个组件。我将其改写为:
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
无论如何都不会使用GetHashCode
。(您绝对应该适当地覆盖它,但它不会成为错误的原因。)
如果你能展示一个简短但完整的程序来演示这个问题,那将非常有帮助——我强烈怀疑你会发现这实际上是测试数据的问题。在用户[userIndexer-1].Licenses.Add(items[3])
之后,用户[userIndexer-1]
不再是同一个用户了。您已经更改了用于相等比较的许可证(在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;
}
}
确保对象的特定实例的GetHashCode
返回的值永远不会更改,这一点非常重要。如果值更改,则列表和字典将无法正常工作
将GetHashCode
视为“GetPrimaryKey”。如果有人向用户添加了新的许可证,则不会更改数据库中用户记录的主键。同样,您也不能更改GetHashCode
从代码中可以看出,您正在更改许可证集合,并使用该集合计算哈希代码。所以这可能是你的问题所在
现在,为您生成的每个哈希代码使用一个常量是完全合法的——例如,您可以为每个实例返回42
。这将强制调用Equals
,以确定两个对象是否相等。使用不同的散列码所能做的就是缩短调用Equals
的需要
如果\u userName
字段没有更改,则只需返回其哈希代码,然后查看它是否正常工作。许可证列表是否更改?\u用户名
是否更改?您是否已进入调试器查看哪一行产生了不正确的结果?如果(!\u licenses.Any()&&!\u userName.Equals(“”)),我会删除,因为它非常无用,但除此之外,似乎还可以。您确定您的许可证的顺序相同吗?您的意思是什么?我浏览了一个日志文件,当前用户在读取该文件时不断更改。由于某些原因,Contains只工作一次(第一次),当currentUser更改时,用户列表的内部也会更改。我想这不是很清楚,所以我将在编辑中发布相应的代码。@L.B我不明白你的意思,你能进一步解释吗?:-)是的。我都是在用户类中完成的。用户:我有一个很好的观点,不过我忘了提。这里没有许可证类别。许可证是一个公共字符串。如果我不能使用SequenceEqual,我能用什么?哦,那么它应该可以正常工作,我唯一能想到的是许可证的顺序是否错误。我推荐调试器。非常好的建议,我会尝试在一个新项目中单独使用它,并将向您发布。我无法制作一个简短但完整的程序,因为这可能需要再次制作一个几乎相同的项目。我认为这与它的顺序敏感有关,你知道我怎样才能使它对顺序不敏感吗?:)@乔兹:为什么要再次创建同一个项目?它只需要用户类和一个小小的控制台应用程序就可以创建两个你认为应该平等但却不平等的用户。至于顺序敏感性-如果这些是一组许可证,那么我建议将它们保存在HashSet
中-然后可以使用SetEquals
方法。好的,问题似乎已经解决了,但我不确定的是许可证顺序。无论如何,非常感谢您的帮助和时间@Joze:你真的应该决定如何考虑许可证的收集。一些选项:总是排序;任意的命令,但它很重要;任意的不重要的顺序(集合)。然后,您可以根据自己的需要使用合适的收藏。(你也应该考虑是否允许复制。)哦,现在我明白了,谢谢。如果它影响了,我会检查一下。只是在用户名相同的情况下,我才添加许可证,这对我来说是有意义的。不管用户是否相同(许可证不同),重要的是他们的用户名相同,这样就可以添加新的许可证,而不是添加一个全新的用户。你有什么建议来解决这个问题吗?只是在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;
}
}