6

HashSetsC# で作業しているときに、最近厄介な問題に遭遇HashSetsしました。要素の単一性を保証しません。それらはセットではありません。彼らが保証しているのは、Add(T item)が呼び出されたときに、セット内のアイテムが である場合、アイテムが追加されないことitem.equals(that)ですtrue。これは、すでにセット内にあるアイテムを操作した場合には当てはまりません。デモを行う小さなプログラム (私の Linqpad からのコピーパスタ):

void Main()
{
    HashSet<Tester> testset = new HashSet<Tester>();
    testset.Add(new Tester(1));
    testset.Add(new Tester(2));
    foreach(Tester tester in testset){
      tester.Dump();
    }
    foreach(Tester tester in testset){
      tester.myint = 3;
    }
    foreach(Tester tester in testset){
      tester.Dump();
    }
    HashSet<Tester> secondhashset = new HashSet<Tester>(testset);
    foreach(Tester tester in secondhashset){
      tester.Dump();
    }
}

class Tester{
  public int myint;

  public Tester(int i){
    this.myint = i;
  }

  public override bool Equals(object o){
    if (o== null) return false;
    Tester that = o as Tester;
    if (that == null) return false;
    return (this.myint == that.myint);
  }

  public override int GetHashCode(){
    return this.myint;
  }

  public override string ToString(){
    return this.myint.ToString();
  }
}

コレクション内のアイテムが等しくなるように喜んで操作し、新しい HashSet が構築されたときにのみそれらを除外します。エントリが一意であることを知る必要があるセットで作業したい場合、何が適切ですか? Add(T item) がアイテムのコピーを追加し、列挙子が含まれているアイテムのコピーを列挙する場合、独自のロールを作成しますか? これは、含まれるすべての要素が、少なくとも要素の等価性に影響を与える項目において、ディープ コピー可能でなければならないという課題を提示します。

別の解決策は、独自のロールを作成し、INotifyPropertyChanged を実装する要素のみを受け入れ、イベントに対してアクションを実行して等しいかどうかを再確認することですが、これは非常に制限されているように見えます。 .

私が考えたさらに別の可能な解決策は、コンストラクターですべてのフィールドが読み取り専用または const であることを確認することです。すべてのソリューションには、非常に大きな欠点があるようです。他に選択肢はありますか?

4

3 に答える 3

6

あなたは本当にオブジェクトのアイデンティティについて話しているのです。アイテムをハッシュする場合は、比較できるように、何らかのIDが必要です。

  • それが変更された場合、それは有効なIDメソッドではありません。あなたは現在持っていpublic int myintます。それは本当にあるべきでありreadonly、コンストラクターでのみ設定されます。
  • 2つのオブジェクトが概念的に異なる場合(つまり、特定の設計でそれらを異なるものとして扱いたい場合)、それらのハッシュコードは異なる必要があります。
  • 同じコンテンツを持つ2つのオブジェクト(つまり、同じフィールド値を持つ2つの値オブジェクト)がある場合、それらは同じハッシュコードを持ち、等しくなければなりません。
  • データモデルで、同じコンテンツを持つ2つのオブジェクトを持つことができるが、それらを等しくすることはできないと示されている場合は、コンテンツをハッシュするのではなく、代理IDを使用する必要があります。
  • おそらく、オブジェクトは変更できないように、オブジェクトは不変の値型である必要があります
  • それらが可変タイプである場合は、特定のオブジェクトに対して変更されないサロゲートID(つまり、カウンターIDの増加やオブジェクトのハッシュコードの使用など、外部から導入されるID)を割り当てる必要があります。

これはTester、セットではなく、オブジェクトの問題です。あなたはアイデンティティをどのように定義するかについて真剣に考える必要があります。簡単な問題ではありません。

于 2012-07-10T10:13:27.623 に答える
0

保証された一意のアイテムの 1 次元コレクションが必要な場合、私は通常Dictionary<TKey, Tvalue>、同じ要素を追加することはできませんKey。さらに、通常、いくつかのプロパティをアイテムにアタッチする必要がありValue、便利です (私の頼りになる値の型Tuple<>は多くの値...)。

もちろん、これはパフォーマンスが最も優れているわけでもなく、メモリを最も消費しないソリューションでもありませんが、通常、パフォーマンスやメモリに関する懸念はありません。

于 2012-07-10T10:18:15.040 に答える
0

独自のIEqualityComparerを実装し、それを HashSet のコンストラクターに渡して、目的の等値比較子を確実に取得する必要があります。

Joe が言ったように、コレクションを一意のままにしたい場合は .Add(T item)、コンストラクターによって作成され、パブリックに表示されるセット属性を持たない ValueObjects を使用する必要があります。すなわち

于 2012-07-10T10:21:39.630 に答える