I'm using a Syncfusion grid control's selections to get a list of our data objects. When we multi-select objects, for some reason the objects are being double reported. The below code returns one entry for a single selection, four (!) for two selected objects, six (!) for three, etc.
There seems to be a minor bug in the selection code that is causing a multi-selection to be reported twice in this.BaseGridControl.Model.Selections
. Yes, the bug should be fixed. But for now, I can work around that; since the objects these rows represent are going into a HashSet
anyway, they should be deduplicated there.
HashSet<Bean> selectedBeanSet = new HashSet<Bean>();
for (int i = this.BaseGridControl.Model.Selections.Count - 1; i >= 0; i--) {
selection = this.BaseGridControl.Model.Selections.Ranges[i];
topRow = selection.Top;
bottomRow = selection.Bottom;
for (int j = bottomRow; j >= topRow; j--) {
selectedBeanSet.Add(GetObjectAtRow(j));
}
}
But even after doing this, I still have four rows when there should only be two. Interrogating in the immediate window, I can learn a little about what's there:
selectedBeanSet.Take(4).ToList()[3].GetHashCode()
5177447
selectedBeanSet.Take(4).ToList()[1].GetHashCode()
5177447
These are still the same object! I wrote the .GetHashCode() myself — I know it's referring to properties of Bean
and not any kind of reference.
And in case there was any doubt:
selectedBeanSet.GetType().Name
"HashSet`1"
How can the same object be in a HashSet
twice? Is the .GetHashCode()
I'm calling in the immediate window not the one being used as a key in the HashSet
? What's going on here?
EDIT: Here's my .GetHashCode()
implementation. I've put a breakpoint in here and stepped through so I know it's getting hit.
public override int GetHashCode() {
return this.Property1.GetHashCode() ^ this.Property2.GetHashCode() ^ this.Property3.GetHashCode();
}
And .Equals()
:
public override bool Equals(Bean other) {
return this.Property1 == other.Property1 && this.Property2 == other.Property2 && this.Property3 == other.Property3;
}
EDIT 2: The Bean
class, sanitized for public consumption. It's a bit strange since for persistence reasons we're storing everything in struct
s behind the scenes. I believe that fact is immaterial to this problem, however.
public class Bean : AbstractBean<Bean, BeanStruct> {
BeanStruct myStruct;
public int Property1 {
get {
return this.myStruct.property1;
}
set {
this.myStruct.property1 = value;
RaisePropertyChanged("Property1");
}
}
public string Property2 {
get {
return this.myStruct.property2;
}
set {
this.myStruct.property2 = EngineUtils.FillOrTruncate(value, Bean.Length);
RaisePropertyChanged("Property2");
}
}
public string Property3 {
get {
return this.myStruct.property3;
}
set {
this.myStruct.property3 = EngineUtils.FillOrTruncate(value, Bean.Length);
RaisePropertyChanged("Property3");
}
}
}