如何为HashSet正确实现IEqualityComparer ?
本文关键字:实现 IEqualityComparer HashSet | 更新日期: 2023-09-27 17:53:16
我正在编写一些将LDAP属性名称映射到友好名称并返回的代码。只有简单的类叫做DirectoryProperty:
public class DirectoryProperty
{
public string Id { get; set; }
public string Name { get; set; }
public string HelpText {get; set; }
public DirectoryProperty(string id, string name)
{
Id = id;
Name = name;
}
}
然后,我编写了使用HashSet构建这些对象集合的代码。我有一组固定的属性,我提供,但我想允许其他人添加自己的项目。set似乎是一个很好的结构,因为当您查询LDAP时,您不希望有重复的属性,这也适用于用户从属性列表中进行选择的UI。
public class PropertyMapper
{
readonly HashSet<DirectoryProperty> props = new HashSet<DirectoryProperty>(new DirectoryPropertyComparer());
public PropertyMapper() // Will eventually pass data in here
{
props.Add(new DirectoryProperty("displayName", "Display Name"));
props.Add(new DirectoryProperty("displayName", "Display Name")); //err
props.Add(new DirectoryProperty("xyz", "Profile Path")); //err
props.Add(new DirectoryProperty("samAccountName", "User Account Name"));
props.Add(new DirectoryProperty("mobile", "Mobile Number"));
props.Add(new DirectoryProperty("profilePath", "Profile Path"));
}
public List<string> GetProperties()
{
return props.Select(directoryProperty => directoryProperty.Id).OrderBy(p => p).ToList();
}
public List<string> GetFriendlyNames()
{
return props.Select(directoryProperty => directoryProperty.Name).OrderBy(p => p).ToList();
}
}
可以看到,现在构造函数中有两个有问题的数据项。其中第一个是明显的重复,另一个是基于DirectoryProperty的Name属性的重复。
我最初实现的IEqualityComparer是这样的:
class DirectoryPropertyComparer : IEqualityComparer<DirectoryProperty>
{
public bool Equals(DirectoryProperty x, DirectoryProperty y)
{
if (x.Id.ToLower() == y.Id.ToLower() || x.Name.ToLower() == y.Name.ToLower())
{
return true;
}
return false;
}
public int GetHashCode(DirectoryProperty obj)
{
return (obj.Id.Length ^ obj.Name.Length).GetHashCode();
}
}
我能做些什么来确保Id和DirectoryProperty的Name属性都被检查唯一性,以确保基于任何一个的重复被捕获?我可能在这里太严格了,我住在我现有的代码,因为它似乎处理重复Id的OK,但我有兴趣了解更多关于这个。
不清楚你到底想做什么:
- 你的等式方法认为两个值相等,如果它们的名字或它们的id相同
- 你的GetHashCode方法包括两个值,所以(意外冲突除外)你只匹配名称和ID在两个对象 中具有相同的长度
从根本上说,第一种方法是有缺陷的。考虑三个条目:
A = { Name=¨N1¨, ID=¨ID1¨ }
B = { Name=¨N2¨, ID=¨ID1¨ }
C = { Name=¨N2¨, ID=¨ID2¨ }
您似乎想要:
A.Equals(B) - true
B.Equals(C) - true
A.Equals(C) - false
这违反了相等性(及物性)规则。
我强烈建议你有两个集合——一个按ID比较值,另一个按Name比较值。然后编写一个方法,只在中没有出现的情况下才向两个集合添加条目。
这种方法是行不通的。Equals方法需要定义一个等价关系,而这种关系不能用这种方式定义。
等价关系必须是可传递的,但这个关系不是可传递的。
{"A", "B"} == {"C", "B"}
and
{"A", "B"} == {"A", "D"}
but
{"C", "B"} != {"A", "D"}
更好的方法是创建两个字典——一个用于ID,一个用于name——并在添加新值之前检查两个字典是否存在冲突。