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 structs 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");
        }
    }
}
