如何正确实现HashSet的IEqualityComparer?

时间:2011-04-26 15:41:28

标签: c# data-structures c#-3.0

我正在编写一些代码,将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编写代码来构建这些对象的集合。我有一套我提供的fiexed属性但我想允许其他人添加他们自己的项目。一个集合似乎是一个很好的结构,因为当您查询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();
 }
}

正如您所看到的,我现在在构造函数中有2个问题数据项。第一个是明显的重复,另一个是基于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();
  }
}

我能做些什么来确保检查DirectoryProperty的Id和Name属性是否具有唯一性,以确保捕获基于任何一个的重复项?我可能在这里过于严格,我使用现有的代码,因为它似乎处理了重复的Id,但我有兴趣了解更多相关内容。

2 个答案:

答案 0 :(得分:3)

目前还不清楚你想要做什么:

  • 如果 他们的名字他们的ID相同,那么你的相等方法会认为两个值是相等的
  • 您的GetHashCode方法包含两个值,因此(意外冲突除外)只有当两者名称和ID在两个对象中具有相同的长度时才匹配< / LI>

基本上,第一种方法是有缺陷的。考虑三个条目:

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比较值,另一个按名称比较值。然后编写一个方法,只在两个集合中添加一个条目,如果它们 中没有出现。

答案 1 :(得分:0)

这种方法不起作用。 Equals方法需要定义等价关系,并且不能以这种方式定义这种关系。

等价关系必须是可传递的,但这种关系不具有传递性。

{"A", "B"} == {"C", "B"}
and
{"A", "B"} == {"A", "D"}
but
{"C", "B"} != {"A", "D"}

更好的方法是创建两个字典 - 一个用于ID,一个用于Name - 并在添加新值之前检查两个字典是否存在冲突。