1

IPathwayModule という不変の値オブジェクトがあり、その値は次のように定義されています。

  • (整数) ブロック。
  • (エンティティ) モジュール。(文字列) ModuleId で識別されます。
  • (列挙) ステータス。と
  • (エンティティ) (文字列) ClassId で識別されるクラス - null の場合もあります。

これは、いくつかの単体テストで機能するように見える現在の IEqualityComparer 実装です。しかし、自分が何を正しく行っているかを十分に理解しているとは思えません。以前の実装では、繰り返しテストを実行すると失敗することがありました。

private class StandardPathwayModuleComparer : IEqualityComparer<IPathwayModule>
{
    public bool Equals(IPathwayModule x, IPathwayModule y)
    {
        int hx = GetHashCode(x);
        int hy = GetHashCode(y);
        return hx == hy;
    }

    public int GetHashCode(IPathwayModule obj)
    {
        int h;
        if (obj.Class != null)
        {
            h = obj.Block.GetHashCode() + obj.Module.ModuleId.GetHashCode() + obj.Status.GetHashCode() + obj.Class.ClassId.GetHashCode();
        }
        else
        {
            h = obj.Block.GetHashCode() + obj.Module.ModuleId.GetHashCode() + obj.Status.GetHashCode() + "NOCLASS".GetHashCode();
        }
        return h;
    }
}

IPathwayModule は間違いなく不変であり、同じ値を持つ異なるインスタンスは等しく、同じ HashCode を生成する必要があります。それらは HashSet 内の項目として使用されるためです。

私の質問は次のとおりです。

  • この場合、インターフェイスを正しく使用していますか?
  • 望ましい動作が見られない場合はありますか?
  • 堅牢性、パフォーマンスを向上させる方法はありますか?
  • 私が守っていない良い習慣はありますか?
4

7 に答える 7

4

ハッシュ関数の結果に関して Equals を実行しないでください。脆弱すぎるためです。代わりに、フィールドごとにフィールド値の比較を行います。何かのようなもの:

return x != null && y != null && x.Name.Equals(y.Name) && x.Type.Equals(y.Type) ...

また、ハッシュ関数の結果は実際には足し算に適していません。^代わりに演算子を使用してみてください。

return obj.Name.GetHashCode() ^ obj.Type.GetHashCode() ...

GetHashCode の null チェックは必要ありません。その値が null の場合、より大きな問題が発生しています。制御できないものから回復しようとしても無駄です...

于 2009-10-14T11:24:51.597 に答える
3

唯一の大きな問題は、Equals の実装です。ハッシュ コードは一意ではありません。異なるオブジェクトに対して同じハッシュ コードを取得できます。IPathwayModule の各フィールドを個別に比較する必要があります。

GetHashCode() は少し改善できます。int で GetHashCode() を呼び出す必要はありません。int 自体は適切なハッシュ コードです。列挙値についても同じです。GetHashCode は次のように実装できます。

public int GetHashCode(IPathwayModule obj)
{
    unchecked {
        int h = obj.Block + obj.Module.ModeleId.GetHashCode() + (int) obj.Status;
        if (obj.class != null)
           h += obj.Class.ClassId.GetHashCode();
        return h;
    }
}

算術演算でオーバーフローが発生する可能性があるため、「チェックされていない」ブロックが必要です。

于 2009-10-14T11:29:46.343 に答える
2

比較オブジェクトの主な方法として GetHashCode() を使用しないでください。フィールドごとに比較します。

同じハッシュ コードを持つ複数のオブジェクトが存在する可能性があります (これは「ハッシュ コードの衝突」と呼ばれます)。

また、OverflowException が発生しやすいため、複数の整数値を加算する場合は注意してください。「排他的 OR」(^) を使用して、ハッシュコードを結合するか、コードを「チェックされていない」ブロックにラップします。

于 2009-10-14T11:24:30.927 に答える
1

私があなたのことをよく理解していれば、あなたのコードに関するコメントを聞きたいと思うでしょう。ここに私の発言があります:

  1. GetHashCode加算するのではなく、一緒に XOR する必要があります。XOR ( ^) を使用すると、衝突を回避できる可能性が高くなります
  2. ハッシュコードを比較します。それは良いことですが、これを行うのは、基になるオブジェクトがGetHashCode. そうでない場合は、プロパティとそのハッシュコードを使用して結合します。
  3. ハッシュコードは重要であり、迅速な比較を可能にします。ただし、ハッシュ コードが等しい場合でも、オブジェクトは異なる可能性があります。これはめったに起こりません。ただし、ハッシュ コードが等しい場合は、オブジェクトのフィールドを比較する必要があります。
  4. 値の型は不変だと言いますが、不変.Classではないオブジェクト ( )を参照しています。
  5. 最初のテストとして参照比較を追加して、常に比較を最適化します。参照が等しくない、オブジェクトが等しくない、次に構造体が等しくない。

ポイント5は、値の型で参照するオブジェクトが同じ参照でない場合に等しくないことを返すかどうかによって異なります。

編集:多くの文字列を比較します。文字列比較は C# で最適化されています。他の人が示唆==したように、比較でそれらをより適切に使用できます。GetHashCode については、^他の人が提案したように OR も使用します。

于 2009-10-14T11:32:10.143 に答える
1

Equals と GetHashCode のより良いバージョンを実装する必要があります。

たとえば、enum のハッシュ コードは単にその数値です。

つまり、これら 2 つの列挙型を使用すると、次のようになります。

public enum A { x, y, z }
public enum B { k, l, m }

次に、実装で、次の値の型:

public struct AB {
    public A;
    public B;
}

次の 2 つの値は等しいと見なされます。

AB ab1 = new AB { A = A.x, B = B.m };
AB ab2 = new AB { A = A.z, B = B.k };

あなたはそれを望んでいないと思います。

また、値の型をインターフェイスとして渡すと、それらがボックス化されます。これは、おそらくそれほどではありませんが、パフォーマンスの問題を引き起こす可能性があります。IEqualityComparer の実装で値の型を直接受け取るようにすることを検討してください。

于 2009-10-14T11:25:19.117 に答える
1
  1. 2 つのオブジェクトのハッシュ コードが等しいからといって、2 つのオブジェクトが等しいと仮定するのは誤りです。すべてのメンバーを個別に比較する必要があります
  2. ハッシュ コードを結合するには、+ ではなく ^ を使用する方がよいでしょう。
于 2009-10-14T11:52:43.380 に答える