web-dev-qa-db-ja.com

カスタムIEqualityComparerを使用したIEnumerable <T>でのC#の区別

これが私がやろうとしていることです。 LINQ to XMLを使用してXMLファイルをクエリしているため、IEnumerable<T>オブジェクト。Tは私の「Village」クラスであり、このクエリの結果で埋められます。いくつかの結果が重複しているため、次のようにIEnumerableオブジェクトでDistinct()を実行します。

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);
    }
}

デフォルトの比較器はVillageオブジェクトでは機能しないため、ここで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
}

Distinct()メソッドは機能しません。これは、それを使用してもしなくてもまったく同じ数の結果が得られるからです。別のこと、そしてそれが通常可能かどうかはわかりませんが、AllianceComparer.Equals()に足を踏み入れて、何が問題になるのかを知ることはできません。
インターネットでこの例を見つけましたが、実装を機能させることができません。

うまくいけば、ここの誰かがここで何が間違っているのかを見るかもしれません!前もって感謝します!

52
Fueled

問題はGetHashCodeにあります。代わりにAllianceNameのハッシュコードを返すように変更する必要があります。

int IEqualityComparer<Village>.GetHashCode(Village obj)
{
    return obj.AllianceName.GetHashCode();
}

問題は、Equalstrueを返す場合、オブジェクトは同じVillageを持つ異なるAllianceNameオブジェクトの場合ではない同じハッシュコードを持つ必要があります。 Distinctはハッシュテーブルを内部で構築することで機能するため、ハッシュコードが異なるために一致しないオブジェクトが同じになることになります。

同様に、2つのファイルを比較するために、2つのファイルのハッシュが同じでない場合、ファイル自体をチェックする必要はまったくありません。彼ら 意志 異なる。それ以外の場合は、それらが本当に同じであるかどうかを確認し続けます。それが、Distinctが使用するハッシュテーブルの動作です。

72
Mehrdad Afshari

return alliances.Select(v => v.AllianceName).Distinct();

それはIEnumerable<string>ではなくIEnumerable<Village>を返します。

11
superrcat

または、行を変更します

return alliances.Distinct(new AllianceComparer());

return alliances.Select(v => v.AllianceName).Distinct();
6
Jacob Seleznev