HashSets nie zachowują unikalności elementów, jeśli zmutujesz ich tożsamość

Podczas pracy zHashSets w C # niedawno natknąłem się na irytujący problem:HashSets nie gwarantuj jedności elementów; nie są to zestawy. To, co gwarantują, to kiedyAdd(T item) jest nazywany, element nie jest dodawany, jeśli dla dowolnego elementu w zestawieitem.equals(that) jesttrue. Nie działa to dłużej, jeśli manipulujesz przedmiotami już w zestawie. Mały program, który demonstruje (copypasta z mojego Linqpada):

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

Chętnie manipuluje elementami w kolekcji, aby być równym, filtrując je tylko wtedy, gdy zostanie zbudowany nowy HashSet. Co jest godne polecenia, gdy chcę pracować z zestawami, w których muszę wiedzieć, że wpisy są wyjątkowe? Rzuć własne, gdzie Dodaj (element T) dodaje kopię elementu, a moduł wyliczający wylicza kopie zawartych elementów? Stawia to wyzwanie, że każdy zawarty element powinien być głęboko kopiowalny, przynajmniej w jego elementach, które wpływają na jego równość.

Innym rozwiązaniem byłoby przetaczanie własnych i akceptowanie tylko elementów, które implementują INotifyPropertyChanged, i podejmowanie działań w celu ponownego sprawdzenia równości, ale wydaje się to poważnie ograniczające, nie wspominając o dużej ilości pracy i utraty wydajności pod maską .

Jeszcze innym możliwym rozwiązaniem, o którym myślałem, jest upewnienie się, że wszystkie pola są tylko do odczytu lub const w konstruktorze. Wszystkie rozwiązania wydają się mieć bardzo duże wady. Czy mam jakieś inne opcje?

questionAnswers(3)

yourAnswerToTheQuestion