title: Is Dictionary broken or should GetHashCode() only base on immutable members?
body: <p>When an object is added to the .NET <a href="http://msdn.microsoft.com/en-us/library/xfhwa508.aspx" rel="noreferrer">System.Collections.Generic.Dictionary</a> class the hashcode of the key is stored internally and used for later comparisons.  When the hashcode changes after its initial insertion into the dictionary it often becomes "inaccessible" and may surprise its users when an existence check, even using the same reference, returns false (sample code below).</p>
  
  <p>The <a href="http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx" rel="noreferrer">GetHashCode</a> documentation says:</p>
  
  <blockquote>
    <p>The GetHashCode method for an object must consistently return the same hash code as long as there is no modification to the object state that determines the return value of the object's Equals method.</p>
  </blockquote>
  
  <p>So, according to the <code>GetHashCode</code> docs, the hashcode may change whenever <a href="http://msdn.microsoft.com/en-us/library/ms173147(v=vs.80).aspx" rel="noreferrer">equality</a>-determining state is changed, yet the <code>Dictionary</code> implementation does not support this.</p>
  
  <p>Is the current .NET dictionary implementation broken in that it incorrectly ignore the hashcode allowances? Should <code>GetHashCode()</code> only be based on immutable members? Or, is there something else to break a possible false dichotomy?</p>
  
  <pre><code>class Hashable
  {
      public int PK { get; set; }
  
      public override int GetHashCode()
      {
          if (PK != 0) return PK.GetHashCode();
          return base.GetHashCode();
      }
  
      public override bool Equals(object obj)
      {
          return Equals(obj as Hashable);
      }
  
      public virtual bool Equals(Hashable other)
      {
          if (other == null) return false;
          else if (ReferenceEquals(this, other)) return true;
          else if (PK != 0 &amp;&amp; other.PK != 0) return Equals(PK, other.PK);
          return false;
      }
  
      public override string ToString()
      {
          return string.Format("Hashable {0}", PK);
      }
  }
  
  class Test
  {
      static void Main(string[] args)
      {
          var dict = new Dictionary&lt;Hashable, bool&gt;();
          var h = new Hashable();
          dict.Add(h, true);
  
          h.PK = 42;
          if (!dict.ContainsKey(h)) // returns false, despite same reference
              dict.Add(h, false);
      }
  }
  </code></pre>