Eu tenho uma classe que é IComparable
:
public class a : IComparable
{
public int Id { get; set; }
public string Name { get; set; }
public a(int id)
{
this.Id = id;
}
public int CompareTo(object obj)
{
return this.Id.CompareTo(((a)obj).Id);
}
}
Quando adiciono uma lista de objetos desta classe a um conjunto de hash:
a a1 = new a(1);
a a2 = new a(2);
HashSet<a> ha = new HashSet<a>();
ha.add(a1);
ha.add(a2);
ha.add(a1);
Está tudo bem e ha.count
está 2
, mas:
a a1 = new a(1);
a a2 = new a(2);
HashSet<a> ha = new HashSet<a>();
ha.add(a1);
ha.add(a2);
ha.add(new a(1));
Agora ha.count
é 3
.
- Por que não
HashSet
respeitaa
oCompareTo
método? - É
HashSet
a melhor maneira de ter uma lista de objetos exclusivos?
IEqualityComparer<T>
no construtor ou implementá-lo na classea
. msdn.microsoft.com/pt-br/library/bb301504(v=vs.110).aspxRespostas:
Ele usa um
IEqualityComparer<T>
(aEqualityComparer<T>.Default
menos que você especifique um diferente na construção).Quando você adiciona um elemento ao conjunto, ele encontra o código hash usando
IEqualityComparer<T>.GetHashCode
e armazena o código hash e o elemento (depois de verificar se o elemento já está no conjunto, é claro).Para procurar um elemento, ele primeiro usará o
IEqualityComparer<T>.GetHashCode
para encontrar o código de hash e, em seguida, para todos os elementos com o mesmo código de hash, ele será usadoIEqualityComparer<T>.Equals
para comparar a igualdade real.Isso significa que você tem duas opções:
IEqualityComparer<T>
para o construtor. Essa é a melhor opção se você não puder modificar aT
si mesmo ou se desejar uma relação de igualdade não padrão (por exemplo, "todos os usuários com um ID de usuário negativo são considerados iguais"). Isso quase nunca é implementado no próprio tipo (istoFoo
é, não implementaIEqualityComparer<Foo>
), mas em um tipo separado, que é usado apenas para comparações.GetHashCode
eEquals(object)
. Idealmente, implemente tambémIEquatable<T>
no tipo, principalmente se for um tipo de valor. Esses métodos serão chamados pelo comparador de igualdade padrão.Observe como nada disso é em termos de comparação ordenada - o que faz sentido, pois certamente existem situações em que você pode especificar facilmente a igualdade, mas não uma ordenação total. Isso é tudo igual
Dictionary<TKey, TValue>
, basicamente.Se você deseja um conjunto que use ordenação em vez de apenas comparações de igualdade, use o
SortedSet<T>
.NET 4 - que permite especificar um emIComparer<T>
vez de umIEqualityComparer<T>
. Isso usaráIComparer<T>.Compare
- que delegaráIComparable<T>.CompareTo
ouIComparable.CompareTo
se você estiver usandoComparer<T>.Default
.fonte
IEqualityComparer<T>.GetHashCode/Equals()
é implementarEquals
eGetHashCode
sobreT
si mesma (e enquanto você faz isso, você também implementaria a contraparte fortemente tipada : -bool IEquatable<T>.Equals(T other)
)Equals
eGetHashCode
é suficiente - como mencionado na resposta de @ tyriker.IComparable
(ou,IComparer
nesse caso), você não deve ser solicitado a implementar a igualdade separadamente (mas apenasGetHashCode
). Em certo sentido, as interfaces de comparabilidade devem herdar das interfaces de igualdade. Eu entendo os benefícios de desempenho em ter duas funções separadas (onde você pode otimizar a igualdade separadamente apenas dizendo se algo é igual ou não), mas ainda assim .. Muito confuso caso contrário, quando você especificar quando as instâncias são iguais emCompareTo
função e estrutura não serão consideradas aquele.a.boolProp == b.boolProp ? 1 : 0
ou deveria sera.boolProp == b.boolProp ? 0 : -1
oua.boolProp == b.boolProp ? 1 : -1
. Yuk!Aqui estão os esclarecimentos sobre uma parte da resposta que não foi dita: O tipo de objeto seu
HashSet<T>
não precisa ser implementado,IEqualityComparer<T>
mas apenas substituídoObject.GetHashCode()
eObject.Equals(Object obj)
.Em vez disso:
Você faz isso:
É sutil, mas isso me levou a maior parte do dia tentando fazer com que o HashSet funcionasse da maneira que se destina. E, como outros já disseram,
HashSet<a>
acabará ligandoa.GetHashCode()
ea.Equals(obj)
conforme necessário ao trabalhar com o aparelho.fonte
bool IEquatable<T>.Equals(T other)
para obter um leve ganho de eficiência, mas mais importante ainda, o benefício da clareza. Por razões OBV, além da necessidade de implementarGetHashCode
ao ladoIEquatable<T>
, o doc para IEquatable <T> menciona que, para fins de consistência você também deve substituir oobject.Equals
de consistênciaovveride getHashcode
obras, masoverride bool equals
recebe o erro: nenhum método encontrado para substituição. qualquer ideia?public class a : IEqualityComparer<a> {
e, em seguidanew HashSet<a>(a)
.HashSet
usaEquals
eGetHashCode()
.CompareTo
é para conjuntos encomendados.Se você deseja objetos únicos, mas não se importa com a ordem de iteração deles,
HashSet<T>
geralmente é a melhor opção.fonte
O construtor HashSet recebe o objeto que implementa IEqualityComparer para adicionar novo objeto. se você deseja usar o método no HashSet, anula a substituição de Equals, GetHashCode
fonte