3

ベクトル間の距離行列を として表す 2D 配列を格納していDictionary<DistanceCell, double>ます。私の実装にDistanceCellは、比較されるベクトルを表す 2 つの文字列フィールドがあります。

class DistanceCell
{
    public string Group1 { get; private set; }
    public string Group2 { get; private set; }

    public DistanceCell(string group1, string group2)
    {
        if (group1 == null)
        {
            throw new ArgumentNullException("group1");
        }

        if (group2 == null)
        {
            throw new ArgumentNullException("group2");
        }

        this.Group1 = group1;
        this.Group2 = group2;
    }
}

このクラスをキーとして使用しているので、 and をオーバーライドEquals()しましたGetHashCode():

public override bool Equals(object obj)
{
    // False if the object is null
    if (obj == null)
    {
        return false;
    }

    // Try casting to a DistanceCell. If it fails, return false;
    DistanceCell cell = obj as DistanceCell;
    if (cell == null)
    {
        return false;
    }

    return (this.Group1 == cell.Group1 && this.Group2 == cell.Group2) 
           || (this.Group1 == cell.Group2 && this.Group2 == cell.Group1);
}

public bool Equals(DistanceCell cell)
{
    if (cell == null)
    {
        return false;
    }

    return (this.Group1 == cell.Group1 && this.Group2 == cell.Group2) 
           || (this.Group1 == cell.Group2 && this.Group2 == cell.Group1);
}

public static bool operator ==(DistanceCell a, DistanceCell b)
{
    // If both are null, or both are same instance, return true.
    if (System.Object.ReferenceEquals(a, b))
    {
        return true;
    }

    // If either is null, return false.
    // Cast a and b to objects to check for null to avoid calling this operator method
    // and causing an infinite loop.
    if ((object)a == null || (object)b == null)
    {
        return false;
    }

    return (a.Group1 == b.Group1 && a.Group2 == b.Group2) 
           || (a.Group1 == b.Group2 && a.Group2 == b.Group1);
}

public static bool operator !=(DistanceCell a, DistanceCell b)
{
    return !(a == b);
}

public override int GetHashCode()
{
    int hash;
    unchecked
    {
        hash = Group1.GetHashCode() * Group2.GetHashCode();
    }

    return hash;
}

ご覧のとおり、 の要件の 1 つはDistanceCellGroup1Group2が交換可能であることです。したがって、2 つの文字列xとはyDistanceCell("x", "y")等しくなければなりませんDistanceCell("y", "x")。これが、等しい必要がGetHashCode()あるため、乗算を実装した理由です。DistanceCell("x", "y").GetHashCode()DistanceCell("y", "x").GetHashCode()

私が抱えている問題は、おおよそ 90% の時間は正しく動作しますが、残りの時間はaKeyNotFoundExceptionまたは aをスローすることです。NullReferenceException前者はディクショナリからキーを取得するときにスローされ、後者はforeachループを使用してディクショナリを反復処理し、null のキーを取得して呼び出しEquals()を試みるときにスローされます。これは私の実装のエラーと関係があると思われますが、GetHashCode()確信は持てません。また、アルゴリズムの性質上、キーをチェックしたときに辞書にキーが存在しないというケースは決してないはずです。アルゴリズムは、実行ごとに同じパスを取ります。

アップデート

問題が修正されたことを全員に更新したかっただけです。Equals() または GetHashCode() の実装とは何の関係もないことがわかりました。大規模なデバッグを行ったところ、KeyNotFoundException が発生した理由は、そもそもキーが辞書に存在しなかったためであることがわかりました。問題は、複数のスレッドを使用してディクショナリにキーを追加していたことでした。これによれば、c# ディクショナリ クラスはスレッドセーフではありません。したがって、Add() が失敗したため、キーがディクショナリに追加されなかったタイミングは完璧だったに違いありません。これは、foreach ループがときどき null キーを生成していたことも説明できると思います。Add()'

助けてくれてありがとう!完全に私のせいで終わってしまい申し訳ありません。

4

3 に答える 3

4

EricLippertによるこのブログ投稿をご覧ください

オブジェクトのコンテンツを変更しても、GetHashCodeの結果は決して変更されないはずだと書かれています。その理由は、Dictionaryがより高速なインデックス作成のためにバケットを使用しているためです。GetHasCodeの結果を変更すると、ディクショナリはオブジェクトに適切なバケットを見つけることができなくなり、「キーが見つかりません」という原因になる可能性があります。

私は間違っているかもしれませんが、テストする価値があります。

于 2013-01-20T20:18:27.957 に答える
2

あなたに欠けているのはこの答えだと思います オブジェクトを一般的な辞書キーとして使用する

おそらくIEquatableインターフェースを実装しているとは言っていないでしょう

于 2013-01-20T20:03:27.283 に答える
0

私にはあなたのコードは正しいように見えます。((マイクロ)最適化される可能性がありますが、すべての場合に正しいように見えます。) を作成して使用すると、問題が発生するコードを提供できますDictionary<DistanceCell, double>か?問題は、表示されていないコードにある可能性があります。

于 2013-01-20T20:23:57.090 に答える