Generic Value Object Equality


This post was originally published here.

I read a post from Oren the other day where he posted some code for a generic Entity base type that implemented the correct equality logic.  I realized that I’ve needed a generic base type for Value Objects as well.

Value Object Requirements

In the Domain Driven Design space, a Value Object:

  • Has no concept of an identity
    • Two different instances of a Value Object with the same values are considered equal
    • Describes a characteristic of another thing
      • Is immutable</ul> Unfortunately, in nearly all cases I’ve run in to, we can’t use Value Types in .NET to represent Value Objects.  Value Types (struct) have some size limitations (~16 bytes or less), which we run into pretty quickly.  Instead, we can create a Reference Type (class) with Value Type semantics, similar to the .NET String type.  The String type is a Reference Type, but exhibits Value Type semantics, since it is immutable.  For a Reference Type to exhibit Value Type semantics, it must:

      • Be immutable

        • Override the Equals method, to implement equality instead of identity, which is the default</ul> Additionally, Framework Design Guidelines has some additional requirements I must meet:

        • Provide a reflexive, transitive, and symmetric implementation of Equals

          • Override GetHashCode
            • Implement IEquatable
              • Override the equality operators</ul>

                Generic Implementation

              What I wanted was a base class that would give me all of the Framework Design Guidelines requirements as well as the Domain Driven Design requirements, without any additional logic from concrete types.  Here’s what I ended up with:

              public abstract class ValueObject<T> : IEquatable<T>
                  where T : ValueObject<T>
              {
                  public override bool Equals(object obj)
                  {
                      if (obj == null)
                          return false;

                      T other = obj as T;

                      return Equals(other);
                  }

                  public override int GetHashCode()
                  {
                      IEnumerable<FieldInfo> fields = GetFields();

                      int startValue = 17;
                      int multiplier = 59;

                      int hashCode = startValue;

                      foreach (FieldInfo field in fields)
                      {
                          object value = field.GetValue(this);

                          if (value != null)
                              hashCode = hashCode * multiplier + value.GetHashCode();
                      }

                      return hashCode;
                  }

                  public virtual bool Equals(T other)
                  {
                      if (other == null)
                          return false;

                      Type t = GetType();
                      Type otherType = other.GetType();

                      if (t != otherType)
                          return false;

                      FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

                      foreach (FieldInfo field in fields)
                      {
                          object value1 = field.GetValue(other);
                          object value2 = field.GetValue(this);

                          if (value1 == null)
                          {
                              if (value2 != null)
                                  return false;
                          }
                          else if (! value1.Equals(value2))
                              return false;
                      }

                      return true;
                  }

                  private IEnumerable<FieldInfo> GetFields()
                  {
                      Type t = GetType();

                      List<FieldInfo> fields = new List<FieldInfo>();

                      while (t != typeof(object))
                      {
                          fields.AddRange(t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public));

                          t = t.BaseType;
                      }

                      return fields;
                  }

                  public static bool operator ==(ValueObject<T> x, ValueObject<T> y)
                  {
                      return x.Equals(y);
                  }

                  public static bool operator !=(ValueObject<T> x, ValueObject<T> y)
                  {
                      return ! (x == y);
                  }
              }

</pre> </div>

                    I borrowed a little bit from the .NET [ValueType](http://msdn2.microsoft.com/en-us/library/system.valuetype.aspx) base class for the implementation of Equals.&nbsp; The ValueObject<T> type uses reflection to access and compare all internal fields for Equals, as well as for GetHashCode.&nbsp; All implementers will need to do is to ensure that their concrete type is immutable, and they&#8217;re done.
                    
                    I could probably optimize the reflection calls and cache them, but this implementation is mainly for reference anyway.
                    
                    ### The Tests
                    
                    Just for completeness, I&#8217;ll include the set of [NUnit](http://www.nunit.org/) tests I used to write this class up.&nbsp; I think the tests describe the intended behavior well enough.
                    
                    <div class="CodeFormatContainer">
                      <pre>[TestFixture]<br /> <span class="kwrd">public</span> <span class="kwrd">class</span> ValueObjectTests<br /> {<br /> &nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">private</span> <span class="kwrd">class</span> Address : ValueObject&lt;Address&gt;<br /> &nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">private</span> <span class="kwrd">readonly</span> <span class="kwrd">string</span> _address1;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">private</span> <span class="kwrd">readonly</span> <span class="kwrd">string</span> _city;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">private</span> <span class="kwrd">readonly</span> <span class="kwrd">string</span> _state;<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> Address(<span class="kwrd">string</span> address1, <span class="kwrd">string</span> city, <span class="kwrd">string</span> state)<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_address1 = address1;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_city = city;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_state = state;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> <span class="kwrd">string</span> Address1<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;get { <span class="kwrd">return</span> _address1; }<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> <span class="kwrd">string</span> City<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;get { <span class="kwrd">return</span> _city; }<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> <span class="kwrd">string</span> State<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;get { <span class="kwrd">return</span> _state; }<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /> &nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">private</span> <span class="kwrd">class</span> ExpandedAddress : Address<br /> &nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">private</span> <span class="kwrd">readonly</span> <span class="kwrd">string</span> _address2;<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> ExpandedAddress(<span class="kwrd">string</span> address1, <span class="kwrd">string</span> address2, <span class="kwrd">string</span> city, <span class="kwrd">string</span> state)<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;: <span class="kwrd">base</span>(address1, city, state)<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_address2 = address2;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> <span class="kwrd">string</span> Address2<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;get { <span class="kwrd">return</span> _address2; }<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;[Test]<br /> &nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> <span class="kwrd">void</span> AddressEqualsWorksWithIdenticalAddresses()<br /> &nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address = <span class="kwrd">new</span> Address(<span class="str">"Address1"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address2 = <span class="kwrd">new</span> Address(<span class="str">"Address1"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.IsTrue(address.Equals(address2));<br /> &nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;[Test]<br /> &nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> <span class="kwrd">void</span> AddressEqualsWorksWithNonIdenticalAddresses()<br /> &nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address = <span class="kwrd">new</span> Address(<span class="str">"Address1"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address2 = <span class="kwrd">new</span> Address(<span class="str">"Address2"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.IsFalse(address.Equals(address2));<br /> &nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;[Test]<br /> &nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> <span class="kwrd">void</span> AddressEqualsWorksWithNulls()<br /> &nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address = <span class="kwrd">new</span> Address(<span class="kwrd">null</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address2 = <span class="kwrd">new</span> Address(<span class="str">"Address2"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.IsFalse(address.Equals(address2));<br /> &nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;[Test]<br /> &nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> <span class="kwrd">void</span> AddressEqualsWorksWithNullsOnOtherObject()<br /> &nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address = <span class="kwrd">new</span> Address(<span class="str">"Address2"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address2 = <span class="kwrd">new</span> Address(<span class="str">"Address2"</span>, <span class="kwrd">null</span>, <span class="str">"TX"</span>);<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.IsFalse(address.Equals(address2));<br /> &nbsp;&nbsp;&nbsp;&nbsp;}<br /> &nbsp;&nbsp;&nbsp;&nbsp;<br /> &nbsp;&nbsp;&nbsp;&nbsp;[Test]<br /> &nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> <span class="kwrd">void</span> AddressEqualsIsReflexive()<br /> &nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address = <span class="kwrd">new</span> Address(<span class="str">"Address1"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.IsTrue(address.Equals(address));<br /> &nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;[Test]<br /> &nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> <span class="kwrd">void</span> AddressEqualsIsSymmetric()<br /> &nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address = <span class="kwrd">new</span> Address(<span class="str">"Address1"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address2 = <span class="kwrd">new</span> Address(<span class="str">"Address2"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.IsFalse(address.Equals(address2));<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.IsFalse(address2.Equals(address));<br /> &nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;[Test]<br /> &nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> <span class="kwrd">void</span> AddressEqualsIsTransitive()<br /> &nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address = <span class="kwrd">new</span> Address(<span class="str">"Address1"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address2 = <span class="kwrd">new</span> Address(<span class="str">"Address1"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address3 = <span class="kwrd">new</span> Address(<span class="str">"Address1"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.IsTrue(address.Equals(address2));<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.IsTrue(address2.Equals(address3));<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.IsTrue(address.Equals(address3));<br /> &nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;[Test]<br /> &nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> <span class="kwrd">void</span> AddressOperatorsWork()<br /> &nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address = <span class="kwrd">new</span> Address(<span class="str">"Address1"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address2 = <span class="kwrd">new</span> Address(<span class="str">"Address1"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address3 = <span class="kwrd">new</span> Address(<span class="str">"Address2"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.IsTrue(address == address2);<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.IsTrue(address2 != address3);<br /> &nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;[Test]<br /> &nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> <span class="kwrd">void</span> DerivedTypesBehaveCorrectly()<br /> &nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address = <span class="kwrd">new</span> Address(<span class="str">"Address1"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ExpandedAddress address2 = <span class="kwrd">new</span> ExpandedAddress(<span class="str">"Address1"</span>, <span class="str">"Apt 123"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.IsFalse(address.Equals(address2));<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.IsFalse(address == address2);<br /> &nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;[Test]<br /> &nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> <span class="kwrd">void</span> EqualValueObjectsHaveSameHashCode()<br /> &nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address = <span class="kwrd">new</span> Address(<span class="str">"Address1"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address2 = <span class="kwrd">new</span> Address(<span class="str">"Address1"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.AreEqual(address.GetHashCode(), address2.GetHashCode());<br /> &nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;[Test]<br /> &nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> <span class="kwrd">void</span> TransposedValuesGiveDifferentHashCodes()<br /> &nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address = <span class="kwrd">new</span> Address(<span class="kwrd">null</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address2 = <span class="kwrd">new</span> Address(<span class="str">"TX"</span>, <span class="str">"Austin"</span>, <span class="kwrd">null</span>);<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode());<br /> &nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;[Test]<br /> &nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> <span class="kwrd">void</span> UnequalValueObjectsHaveDifferentHashCodes()<br /> &nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address = <span class="kwrd">new</span> Address(<span class="str">"Address1"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address2 = <span class="kwrd">new</span> Address(<span class="str">"Address2"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode());<br /> &nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;[Test]<br /> &nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> <span class="kwrd">void</span> TransposedValuesOfFieldNamesGivesDifferentHashCodes()<br /> &nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address = <span class="kwrd">new</span> Address(<span class="str">"_city"</span>, <span class="kwrd">null</span>, <span class="kwrd">null</span>);<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Address address2 = <span class="kwrd">new</span> Address(<span class="kwrd">null</span>, <span class="str">"_address1"</span>, <span class="kwrd">null</span>);<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode());<br /> &nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;[Test]<br /> &nbsp;&nbsp;&nbsp;&nbsp;<span class="kwrd">public</span> <span class="kwrd">void</span> DerivedTypesHashCodesBehaveCorrectly()<br /> &nbsp;&nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ExpandedAddress address = <span class="kwrd">new</span> ExpandedAddress(<span class="str">"Address99999"</span>, <span class="str">"Apt 123"</span>, <span class="str">"New Orleans"</span>, <span class="str">"LA"</span>);<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ExpandedAddress address2 = <span class="kwrd">new</span> ExpandedAddress(<span class="str">"Address1"</span>, <span class="str">"Apt 123"</span>, <span class="str">"Austin"</span>, <span class="str">"TX"</span>);<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode());<br /> &nbsp;&nbsp;&nbsp;&nbsp;}<br /> <br /> }<br />

</pre> </div>

                    #### **UPDATE: 6/27/07**
                    
                      * Changed ValueObject<T> to implement IEquatable<T> instead of IEquatable<ValueObject<T>> 
                          * Equals reflects derived type instead of base type, since C# generics are not covariant (or contravariant), IEquatable<ValueObject<T>> != IEquatable<T>
                          * Changed GetHashCode algorithm to use&nbsp;a calculated hash to cover additional test cases 
                              * Gather parent type field values 
                                  * Fix transposed value bug</ul> 
                                  * Fixed NullReferenceException bug when &#8220;other&#8221; is null for IEquatable<T>.Equals 
                                      * Added tests to cover bugs and fixes</ul>
Disable annoying computer beep