C # Distinto su IEnumerable < T > con IEqualityComparer personalizzato
-
10-07-2019 - |
Domanda
Ecco cosa sto cercando di fare. Sto interrogando un file XML usando LINQ to XML, il che mi dà un < T
> oggetto, dove T è il mio "villaggio" classe, riempita con i risultati di questa query. Alcuni risultati sono duplicati, quindi vorrei eseguire un Distinct () sull'oggetto IEnumerable, in questo modo:
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);
}
}
Poiché il comparatore predefinito non funzionava per l'oggetto Village, ne ho implementato uno personalizzato, come mostrato qui nella 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
}
Il metodo Distinct () non funziona, poiché ho esattamente lo stesso numero di risultati con o senza di esso. Un'altra cosa, e non so se di solito è possibile, ma non posso entrare in AllianceComparer.Equals () per vedere quale potrebbe essere il problema.
Ho trovato esempi di questo su Internet, ma non riesco a far funzionare la mia implementazione.
Speriamo che qualcuno qui possa vedere cosa potrebbe esserci di sbagliato qui! Grazie in anticipo!
Soluzione
Il problema è con il tuo GetHashCode
. Dovresti modificarlo per restituire invece il codice hash di AllianceName
.
int IEqualityComparer<Village>.GetHashCode(Village obj)
{
return obj.AllianceName.GetHashCode();
}
Il fatto è che se Equals
restituisce true
, gli oggetti dovrebbero avere lo stesso codice hash che non è il caso di diversi oggetti Village
con lo stesso AllianceName
. Dato che Distinct
funziona costruendo internamente una tabella hash, finirai con oggetti uguali che non saranno affatto abbinati a causa di diversi codici hash.
Allo stesso modo, per confrontare due file, se l'hash di due file non è lo stesso, non è necessario controllare i file stessi. saranno diversi. Altrimenti, continuerai a controllare per vedere se sono davvero uguali o meno. Questo è esattamente ciò che si comporta la tabella hash che Distinct
utilizza.
Altri suggerimenti
restituisce alleanze.Seleziona (v = > v.AllianceName) .Distinct ();
Ciò restituirebbe un IEnumerable < string >
anziché IEnumerable < Village >
.
O cambia la linea
return alliances.Distinct(new AllianceComparer());
a
return alliances.Select(v => v.AllianceName).Distinct();