69090

WinForms: Binding functionality is broken when overriding GetHashCode in base class

I've simplified my code to the following short example that reproduce the error. I'm trying to bind a value of a derived class to a control. The derived class and the binding logic are:

bindingSource = new BindingSource(); numericUpDown1.DataBindings.Add("Value", bindingSource, nameof(SinglePoint.Val), false, DataSourceUpdateMode.OnPropertyChanged); bindingSource.DataSource = new[] { new SinglePoint(100) }; [...] public class SinglePoint : PointList { public SinglePoint(int val) { points.Add(val); } public int Val { get { return points[0]; } set { if (value > 300) value = 300; //(*) points[0] = value; } } }

As a result, when setting for instance a value of 500 on the NumericUpDown, after leaving it we should see 300 because of line (*). This works if the base class does not implement GetHashCode:

public class PointList { protected List<int> points = new List<int>(); public PointList() { } // UNCOMMENT this and the binding will stop working properly //public override int GetHashCode() //{ // unchecked // { // int hashCode = 17; // foreach (var item in points) // hashCode += 13 * item.GetHashCode(); // return hashCode; // } //} }

However, <strong>if you try to uncomment the GetHashCode implementation, the binding will break</strong>. Meaning that after setting 500 and leaving the NumericUpDown, the control will still show 500, but in reality the underlying value will be 300. Why is this happening and how could I solve it without turning down my custom implementation of GetHashCode?

<strong>EDIT</strong>

As pointed out by some of you, GetHashCode should not change because it is used by the binding mechanism. This reply to the first part of my question. However, how can I make my PointList class satisfying the other rule by which "if two things are equal (Equals(...) == true) then they must return the same value for GetHashCode()". If I have two PointList objects with the same list (sequence) of points I would like them to be found equal (because of other logics in my code), which should imply having the same hash code. How would you do?

Answer1:

Thumb rule:

The integer returned by GetHashCode <strong>must never change</strong> while the object is contained in a data structure that depends on the hash code remaining stable

See Eric Lippert's article here.

In the GetHashCode you should use only readonly or immutable fields; otherwise, the object cannot be used in a Dictionary, Hashtable and other hash-based collections.

Answer2:

WinForms uses the hashcode of the data object and data member to generate a key, which is used to track the data-binded-object in an internal storage. If you change the hash-calculation so that the hash changes when a data member changes value, then previously-generated-key is not updated with it, and is therefor different than the current key. This breaks the data-binding-mechanism.

Source: http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/BindingContext.cs

Answer3:

Instead of initialising hashcode to 17 you should initialise it with base.GetHashCode(). int hashCode = base.GetHashCode(); // not 17

Recommend

  • How sys.exc_info() works?
  • keep selected rows when changing dataset in shiny DT datatable
  • How to make an Ionic content scroll
  • How does `std::terminate` know to treat `std::exception`s specially?
  • Javascript unload page condition
  • CKeditor stripping font tags instead of converting to span
  • Lua: Line breaks in strings
  • Issue with routerLink directive
  • How can I tell a form not to dispose a particular control when it closes?
  • Is there a way to save the selected text and highlight it again once the page is refreshed?
  • SonarQube: Cannot deactivate rule with missing quality profile
  • Laravel: Getting Session ID oddly truncates when using foreach
  • Bypass multiple inheritance in Java
  • Rails Find when some params will be blank
  • D3 nodes and links from JSON with nested arrays of children
  • Recording logins for password protected directories
  • how to do an event when i swipe from fragment to the other
  • Update CALayer sublayers immediately
  • Using $this when not in object context
  • JFileChooser in front of fullscreen Swing application
  • How do I fake an specific browser client when using Java's Net library?
  • How reduce the height of an mschart by breaking up the y-axis
  • Perl system calls when running as another user using sudo
  • Trying to switch camera back to front but getting exception
  • How to format a variable of double type
  • Matrix multiplication with MKL
  • using conditional logic : check if record exists; if it does, update it, if not, create it
  • Hits per day in Google Big Query
  • File not found error Google Drive API
  • Turn off referential integrity in Derby? is it possible?
  • Add sale price programmatically to product variations
  • unknown Exception android
  • costura.fody for a dll that references another dll
  • Observable and ngFor in Angular 2
  • failed to connect to specific WiFi in android programmatically
  • UserPrincipal.Current returns apppool on IIS
  • Unable to use reactive element in my shiny app
  • Converting MP3 duration time
  • java string with new operator and a literal
  • How do I use LINQ to get all the Items that have a particular SubItem?