C # distintos sobre IEnumerable com o costume IEqualityComparer
-
10-07-2019 - |
Pergunta
Aqui está o que estou tentando fazer. Eu estou consultando um arquivo XML usando LINQ to XML, o que me dá uma IEnumerable<T
> objeto, onde T é a minha classe "Village", preenchido com os resultados desta consulta. Alguns resultados são duplicados, então eu gostaria de realizar um distinto () no objeto IEnumerable, assim:
public IEnumerable<Village> GetAllAlliances()
{
try
{
IEnumerable<Village> alliances =
from alliance in xmlDoc.Elements("Village")
where alliance.Element("AllianceName").Value != String.Empty
orderby alliance.Element("AllianceName").Value
select new Village
{
AllianceName = alliance.Element("AllianceName").Value
};
// TODO: make it work...
return alliances.Distinct(new AllianceComparer());
}
catch (Exception ex)
{
throw new Exception("GetAllAlliances", ex);
}
}
Como o comparador padrão não funcionaria para o objeto Village, eu implementei um costume, como visto aqui na classe AllianceComparer:
public class AllianceComparer : IEqualityComparer<Village>
{
#region IEqualityComparer<Village> Members
bool IEqualityComparer<Village>.Equals(Village x, Village y)
{
// Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y))
return true;
// Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return x.AllianceName == y.AllianceName;
}
int IEqualityComparer<Village>.GetHashCode(Village obj)
{
return obj.GetHashCode();
}
#endregion
}
O método distinto () não funciona, como eu tenho exatamente o mesmo número de resultados com ou sem ele. Outra coisa, e eu não sei se é geralmente possível, mas não posso passo para AllianceComparer.Equals () para ver o que poderia ser o problema.
Eu encontrei exemplos disso na internet, mas eu não consigo fazer o meu trabalho de implementação.
Felizmente, alguém aqui pode ver o que poderia estar errado aqui! Agradecemos antecipadamente!
Solução
O problema é com o seu GetHashCode
. Você deve alterá-lo para retornar o código de hash de AllianceName
vez.
int IEqualityComparer<Village>.GetHashCode(Village obj)
{
return obj.AllianceName.GetHashCode();
}
A coisa é, se Equals
retornos true
, os objetos devem ter o mesmo código de hash que não é o caso para Village
objetos diferentes com o mesmo AllianceName
. Desde Distinct
funciona através da construção de uma tabela hash internamente, você vai acabar com objetos iguais que não vai ser combinados em tudo devido a diferentes códigos de hash.
Da mesma forma, para comparar dois arquivos, se o hash de dois arquivos não são os mesmos, você não precisa verificar os próprios arquivos em tudo. Eles ser diferente. Caso contrário, você vai continuar a verificar para ver se eles são realmente o mesmo ou não. Isso é exatamente o que a tabela hash que usa Distinct
se comporta.
Outras dicas
return alliances.Select(v => v.AllianceName).Distinct();
Isso retornaria um IEnumerable<string>
vez de IEnumerable<Village>
.
Ou alterar a linha
return alliances.Distinct(new AllianceComparer());
para
return alliances.Select(v => v.AllianceName).Distinct();