HashSets не сохраняет элементы уникальными, если вы изменяете их идентичность
При работе сHashSets
в C # я недавно столкнулся с досадной проблемой:HashSets
не гарантируют уникальность элементов; они не наборы. Они гарантируют, что когдаAdd(T item)
называется элемент не добавляется, если для какого-либо элемента в набореitem.equals(that)
являетсяtrue
, Это больше не действует, если вы манипулируете элементами, уже находящимися в наборе. Небольшая программа, которая демонстрирует (copypasta из моего 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, и предпринимать действия с событием для повторной проверки на равенство, но это кажется серьезным ограничением, не говоря уже о большой потере работы и производительности под капотом. ,
Еще одно возможное решение, о котором я подумал, - убедиться, что все поля доступны только для чтения или являются постоянными в конструкторе. Все решения, похоже, имеют очень большие недостатки. У меня есть другие варианты?