HashSet<T>确实删除重复项,因为它是一个集合...但仅当您的类型适当地定义了相等性时。
我怀疑“重复”是指“与另一个对象具有相同字段值的对象”-您需要覆盖 Equals/GetHashCode 才能使其工作,和/或实现 IEquatable<Contact>... 或你可以为HashSet<T> 构造函数提供一个IEqualityComparer<Contact>。
您可以只调用Distinct LINQ 扩展方法,而不是使用HashSet<T>。例如:
list = list.Distinct().ToList();
但同样,您需要以某种方式提供适当的平等定义。
这是一个示例实现。注意我是如何使它不可变的(可变类型的相等是奇怪的,因为两个对象可以在一分钟相等而下一分钟不相等)和
制成
字段私有,具有公共属性。最后,我已经密封了这个类——不可变类型通常应该是密封的,它使平等更容易讨论。
using System;
using System.Collections.Generic;
public sealed class Contact : IEquatable<Contact>
{
private readonly string firstName;
public string FirstName { get { return firstName; } }
private readonly string lastName;
public string LastName { get { return lastName; } }
private readonly string phoneNumber;
public string PhoneNumber { get { return phoneNumber; } }
public Contact(string firstName, string lastName, string phoneNumber)
{
this.firstName = firstName;
this.lastName = lastName;
this.phoneNumber = phoneNumber;
}
public override bool Equals(object other)
{
return Equals(other as Contact);
}
public bool Equals(Contact other)
{
if (object.ReferenceEquals(other, null))
{
return false;
}
if (object.ReferenceEquals(other, this))
{
return true;
}
return FirstName == other.FirstName &&
LastName == other.LastName &&
PhoneNumber == other.PhoneNumber;
}
public override int GetHashCode()
{
// Note: *not* StringComparer; EqualityComparer<T>
// copes with null; StringComparer doesn't.
var comparer = EqualityComparer<string>.Default;
// Unchecked to allow overflow, which is fine
unchecked
{
int hash = 17;
hash = hash * 31 + comparer.GetHashCode(FirstName);
hash = hash * 31 + comparer.GetHashCode(LastName);
hash = hash * 31 + comparer.GetHashCode(PhoneNumber);
return hash;
}
}
}
编辑:好的,响应对GetHashCode() 实现的解释请求:
- 我们想结合这个对象的属性的哈希码
- 我们不会在任何地方检查是否为空,因此我们应该假设其中一些可能为空。
EqualityComparer<T>.Default 总是处理这个,这很好......所以我用它来获取每个字段的哈希码。
- 将多个哈希码组合为一个的“加法和乘法”方法是 Josh Bloch 推荐的标准方法。还有很多其他通用的哈希算法,但这个算法适用于大多数应用程序。
- 我不知道您是否在默认情况下在已检查的上下文中进行编译,因此我将计算置于未检查的上下文中。我们真的不在乎重复的乘法/加法是否会导致溢出,因为我们不是在寻找这样的“数量”......只是一个我们可以重复达到相等的数字对象。
顺便说一下,两种处理无效的方法:
public override int GetHashCode()
{
// Unchecked to allow overflow, which is fine
unchecked
{
int hash = 17;
hash = hash * 31 + (FirstName ?? "").GetHashCode();
hash = hash * 31 + (LastName ?? "").GetHashCode();
hash = hash * 31 + (PhoneNumber ?? "").GetHashCode();
return hash;
}
}
或
public override int GetHashCode()
{
// Unchecked to allow overflow, which is fine
unchecked
{
int hash = 17;
hash = hash * 31 + (FirstName == null ? 0 : FirstName.GetHashCode());
hash = hash * 31 + (LastName == null ? 0 : LastName.GetHashCode());
hash = hash * 31 + (PhoneNumber == null ? 0 : PhoneNumber.GetHashCode());
return hash;
}
}