Created
August 21, 2015 06:46
-
-
Save jahmai-ca/8e0fb0b18dd370de7fc0 to your computer and use it in GitHub Desktop.
Playing around with this idea http://www.mechonomics.com/generic-value-object-equality-updated/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Reflection; | |
namespace Project | |
{ | |
public class DomainEqualityComparer : IEqualityComparer | |
{ | |
public static readonly DomainEqualityComparer Default = new DomainEqualityComparer(); | |
public new bool Equals(object x, object y) | |
{ | |
if (ReferenceEquals(x, y)) | |
return true; | |
if (ReferenceEquals(x, null)) | |
return false; | |
if (ReferenceEquals(null, y)) | |
return false; | |
var xProperties = GetProperties(x.GetType()).Distinct(PropertyInfoComparer.Default).ToArray(); | |
var yProperties = GetProperties(y.GetType()).Distinct(PropertyInfoComparer.Default).ToArray(); | |
if (xProperties.Length != yProperties.Length) | |
return false; | |
if (xProperties.Intersect(yProperties, PropertyInfoComparer.Default).Count() != xProperties.Length) | |
return false; | |
foreach (var xProp in xProperties) | |
{ | |
var yProp = yProperties.First(_ => _.Name == xProp.Name); | |
var xValue = xProp.GetValue(x); | |
var yValue = yProp.GetValue(y); | |
if (ReferenceEquals(xValue, yValue)) | |
continue; | |
if (ReferenceEquals(xValue, null)) | |
return false; | |
if (ReferenceEquals(null, yValue)) | |
return false; | |
if (!xValue.Equals(yValue)) | |
return false; | |
} | |
return true; | |
} | |
public int GetHashCode(object obj) | |
{ | |
if (obj == null) | |
throw new ArgumentNullException("obj"); | |
var t = obj.GetType(); | |
var hashCode = 0; | |
foreach (var prop in GetProperties(t)) | |
{ | |
var value = prop.GetValue(obj); | |
hashCode = (hashCode*397) ^ (value == null ? 0 : value.GetHashCode()); | |
} | |
return hashCode; | |
} | |
private static IEnumerable<PropertyInfo> GetProperties(Type t) | |
{ | |
while (t != null && t != typeof (object) && t != typeof (ValueType)) | |
{ | |
foreach (var prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public)) | |
yield return prop; | |
t = t.BaseType; | |
} | |
} | |
private class PropertyInfoComparer : IEqualityComparer<PropertyInfo> | |
{ | |
public static readonly PropertyInfoComparer Default = new PropertyInfoComparer(); | |
public bool Equals(PropertyInfo x, PropertyInfo y) | |
{ | |
return x.Name == y.Name; | |
} | |
public int GetHashCode(PropertyInfo obj) | |
{ | |
return obj.Name.GetHashCode(); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Project | |
{ | |
public abstract class DomainObject | |
{ | |
public override bool Equals(object obj) | |
{ | |
return DomainEqualityComparer.Default.Equals(this, obj); | |
} | |
public override int GetHashCode() | |
{ | |
return DomainEqualityComparer.Default.GetHashCode(this); | |
} | |
public static bool operator ==(DomainObject x, DomainObject y) | |
{ | |
return DomainEqualityComparer.Default.Equals(x, y); | |
} | |
public static bool operator !=(DomainObject x, DomainObject y) | |
{ | |
return !(x == y); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
namespace Project | |
{ | |
[TestClass] | |
public class DomainObjectTests | |
{ | |
[TestMethod] | |
public void AddressEqualsWorksWithIdenticalAddresses() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address1", "Austin", "TX"); | |
Assert.IsTrue(address.Equals(address2)); | |
} | |
[TestMethod] | |
public void AddressEqualsWorksWithNonIdenticalAddresses() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address2", "Austin", "TX"); | |
Assert.IsFalse(address.Equals(address2)); | |
} | |
[TestMethod] | |
public void AddressEqualsWorksWithNulls() | |
{ | |
var address = new Address(null, "Austin", "TX"); | |
var address2 = new Address("Address2", "Austin", "TX"); | |
Assert.IsFalse(address.Equals(address2)); | |
} | |
[TestMethod] | |
public void AddressEqualsWorksWithNullsOnOtherObject() | |
{ | |
var address = new Address("Address2", "Austin", "TX"); | |
var address2 = new Address("Address2", null, "TX"); | |
Assert.IsFalse(address.Equals(address2)); | |
} | |
[TestMethod] | |
public void AddressEqualsIsReflexive() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
Assert.IsTrue(address.Equals(address)); | |
} | |
[TestMethod] | |
public void AddressEqualsIsSymmetric() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address2", "Austin", "TX"); | |
Assert.IsFalse(address.Equals(address2)); | |
Assert.IsFalse(address2.Equals(address)); | |
} | |
[TestMethod] | |
public void AddressEqualsIsTransitive() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address1", "Austin", "TX"); | |
var address3 = new Address("Address1", "Austin", "TX"); | |
Assert.IsTrue(address.Equals(address2)); | |
Assert.IsTrue(address2.Equals(address3)); | |
Assert.IsTrue(address.Equals(address3)); | |
} | |
[TestMethod] | |
public void AddressOperatorsWork() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address1", "Austin", "TX"); | |
var address3 = new Address("Address2", "Austin", "TX"); | |
Assert.IsTrue(address == address2); | |
Assert.IsTrue(address2 != address3); | |
} | |
[TestMethod] | |
public void DerivedTypesBehaveCorrectly() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX"); | |
Assert.IsFalse(address.Equals(address2)); | |
Assert.IsFalse(address == address2); | |
} | |
[TestMethod] | |
public void DerivedTypesBehaveCorrectlyWhenFieldsAreSame() | |
{ | |
var address = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX"); | |
var address2 = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX"); | |
Assert.IsTrue(address.Equals(address2)); | |
Assert.IsTrue(address == address2); | |
} | |
[TestMethod] | |
public void DerivedTypesBehaveCorrectlyWhenNewFieldsAreDifferent() | |
{ | |
var address = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX"); | |
var address2 = new ExpandedAddress("Address1", "Apt 456", "Austin", "TX"); | |
Assert.IsFalse(address.Equals(address2)); | |
Assert.IsFalse(address == address2); | |
} | |
[TestMethod] | |
public void DerivedTypesBehaveCorrectlyWhenInheritedFieldsAreDifferent() | |
{ | |
var address = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX"); | |
var address2 = new ExpandedAddress("Address2", "Apt 123", "Las Vegas", "NV"); | |
Assert.IsFalse(address.Equals(address2)); | |
Assert.IsFalse(address == address2); | |
} | |
[TestMethod] | |
public void EqualValueObjectsHaveSameHashCode() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address1", "Austin", "TX"); | |
Assert.AreEqual(address.GetHashCode(), address2.GetHashCode()); | |
} | |
[TestMethod] | |
public void TransposedValuesGiveDifferentHashCodes() | |
{ | |
var address = new Address(null, "Austin", "TX"); | |
var address2 = new Address("TX", "Austin", null); | |
Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode()); | |
} | |
[TestMethod] | |
public void UnequalValueObjectsHaveDifferentHashCodes() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address2", "Austin", "TX"); | |
Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode()); | |
} | |
[TestMethod] | |
public void TransposedValuesOfFieldNamesGivesDifferentHashCodes() | |
{ | |
var address = new Address("_city", null, null); | |
var address2 = new Address(null, "_address1", null); | |
Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode()); | |
} | |
[TestMethod] | |
public void DerivedTypesHashCodesBehaveCorrectly() | |
{ | |
var address = new ExpandedAddress("Address99999", "Apt 123", "New Orleans", "LA"); | |
var address2 = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX"); | |
Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode()); | |
} | |
private class Address : DomainObject | |
{ | |
private readonly string _address1; | |
private readonly string _city; | |
private readonly string _state; | |
public Address(string address1, string city, string state) | |
{ | |
_address1 = address1; | |
_city = city; | |
_state = state; | |
} | |
public string Address1 | |
{ | |
get { return _address1; } | |
} | |
public string City | |
{ | |
get { return _city; } | |
} | |
public string State | |
{ | |
get { return _state; } | |
} | |
} | |
private class ExpandedAddress : Address | |
{ | |
private readonly string _address2; | |
public ExpandedAddress(string address1, string address2, string city, string state) | |
: base(address1, city, state) | |
{ | |
_address2 = address2; | |
} | |
public string Address2 | |
{ | |
get { return _address2; } | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Reflection; | |
namespace Project | |
{ | |
public class GenericEqualityComparer : IEqualityComparer | |
{ | |
public static readonly GenericEqualityComparer Default = new GenericEqualityComparer(); | |
public new bool Equals(object x, object y) | |
{ | |
if (ReferenceEquals(x, y)) | |
return true; | |
if (ReferenceEquals(x, null)) | |
return false; | |
if (ReferenceEquals(null, y)) | |
return false; | |
var t = x.GetType(); | |
if (t != y.GetType()) | |
return false; | |
return MakeFor(t).Equals(x, y); | |
} | |
public int GetHashCode(object obj) | |
{ | |
if (obj == null) | |
throw new ArgumentNullException("obj"); | |
return MakeFor(obj.GetType()).GetHashCode(obj); | |
} | |
private static IEqualityComparer MakeFor(Type t) | |
{ | |
if (t == null || t == typeof (object) || t == typeof (ValueType)) | |
return BaseEqualityComparer.Default; | |
return (IEqualityComparer) Activator.CreateInstance(typeof (GenericEqualityComparerFor<>).MakeGenericType(t)); | |
} | |
private class GenericEqualityComparerFor<T> : IEqualityComparer | |
{ | |
private static readonly IEqualityComparer Base = MakeFor(typeof (T).BaseType); | |
private static readonly FieldInfo[] Fields = typeof (T).GetFields(BindingFlags.Instance | | |
BindingFlags.NonPublic | | |
BindingFlags.Public); | |
public int GetHashCode(object obj) | |
{ | |
var hashCode = 0; | |
foreach (var field in Fields) | |
{ | |
var value = field.GetValue(obj); | |
hashCode = (hashCode*397) ^ (value == null ? 0 : value.GetHashCode()); | |
} | |
return (hashCode*397) ^ Base.GetHashCode(obj); | |
} | |
public new bool Equals(object x, object y) | |
{ | |
foreach (var field in Fields) | |
{ | |
var value1 = field.GetValue(x); | |
var value2 = field.GetValue(y); | |
if (ReferenceEquals(value1, value2)) | |
continue; | |
if (ReferenceEquals(value1, null)) | |
return false; | |
if (ReferenceEquals(null, value2)) | |
return false; | |
if (!value1.Equals(value2)) | |
return false; | |
} | |
return Base.Equals(x, y); | |
} | |
} | |
private class BaseEqualityComparer : IEqualityComparer | |
{ | |
public static readonly BaseEqualityComparer Default = new BaseEqualityComparer(); | |
public new bool Equals(object x, object y) | |
{ | |
return true; | |
} | |
public int GetHashCode(object obj) | |
{ | |
return 0; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using System.Reflection; | |
namespace Project | |
{ | |
public abstract class GenericObject<T> | |
{ | |
public override bool Equals(object obj) | |
{ | |
return GenericEqualityComparer.Default.Equals(this, obj as GenericObject<T>); | |
} | |
public override int GetHashCode() | |
{ | |
return GenericEqualityComparer.Default.GetHashCode(this); | |
} | |
public static bool operator ==(GenericObject<T> x, GenericObject<T> y) | |
{ | |
return GenericEqualityComparer.Default.Equals(x, y); | |
} | |
public static bool operator !=(GenericObject<T> x, GenericObject<T> y) | |
{ | |
return !(x == y); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
namespace Project | |
{ | |
[TestClass] | |
public class GenericObjectTests | |
{ | |
[TestMethod] | |
public void AddressEqualsWorksWithIdenticalAddresses() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address1", "Austin", "TX"); | |
Assert.IsTrue(address.Equals(address2)); | |
} | |
[TestMethod] | |
public void AddressEqualsWorksWithNonIdenticalAddresses() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address2", "Austin", "TX"); | |
Assert.IsFalse(address.Equals(address2)); | |
} | |
[TestMethod] | |
public void AddressEqualsWorksWithNulls() | |
{ | |
var address = new Address(null, "Austin", "TX"); | |
var address2 = new Address("Address2", "Austin", "TX"); | |
Assert.IsFalse(address.Equals(address2)); | |
} | |
[TestMethod] | |
public void AddressEqualsWorksWithNullsOnOtherObject() | |
{ | |
var address = new Address("Address2", "Austin", "TX"); | |
var address2 = new Address("Address2", null, "TX"); | |
Assert.IsFalse(address.Equals(address2)); | |
} | |
[TestMethod] | |
public void AddressEqualsIsReflexive() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
Assert.IsTrue(address.Equals(address)); | |
} | |
[TestMethod] | |
public void AddressEqualsIsSymmetric() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address2", "Austin", "TX"); | |
Assert.IsFalse(address.Equals(address2)); | |
Assert.IsFalse(address2.Equals(address)); | |
} | |
[TestMethod] | |
public void AddressEqualsIsTransitive() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address1", "Austin", "TX"); | |
var address3 = new Address("Address1", "Austin", "TX"); | |
Assert.IsTrue(address.Equals(address2)); | |
Assert.IsTrue(address2.Equals(address3)); | |
Assert.IsTrue(address.Equals(address3)); | |
} | |
[TestMethod] | |
public void AddressOperatorsWork() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address1", "Austin", "TX"); | |
var address3 = new Address("Address2", "Austin", "TX"); | |
Assert.IsTrue(address == address2); | |
Assert.IsTrue(address2 != address3); | |
} | |
[TestMethod] | |
public void DerivedTypesBehaveCorrectly() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX"); | |
Assert.IsFalse(address.Equals(address2)); | |
Assert.IsFalse(address == address2); | |
} | |
[TestMethod] | |
public void DerivedTypesBehaveCorrectlyWhenFieldsAreSame() | |
{ | |
var address = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX"); | |
var address2 = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX"); | |
Assert.IsTrue(address.Equals(address2)); | |
Assert.IsTrue(address == address2); | |
} | |
[TestMethod] | |
public void DerivedTypesBehaveCorrectlyWhenNewFieldsAreDifferent() | |
{ | |
var address = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX"); | |
var address2 = new ExpandedAddress("Address1", "Apt 456", "Austin", "TX"); | |
Assert.IsFalse(address.Equals(address2)); | |
Assert.IsFalse(address == address2); | |
} | |
[TestMethod] | |
public void DerivedTypesBehaveCorrectlyWhenInheritedFieldsAreDifferent() | |
{ | |
var address = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX"); | |
var address2 = new ExpandedAddress("Address2", "Apt 123", "Las Vegas", "NV"); | |
Assert.IsFalse(address.Equals(address2)); | |
Assert.IsFalse(address == address2); | |
} | |
[TestMethod] | |
public void EqualGenericObjectsHaveSameHashCode() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address1", "Austin", "TX"); | |
Assert.AreEqual(address.GetHashCode(), address2.GetHashCode()); | |
} | |
[TestMethod] | |
public void TransposedValuesGiveDifferentHashCodes() | |
{ | |
var address = new Address(null, "Austin", "TX"); | |
var address2 = new Address("TX", "Austin", null); | |
Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode()); | |
} | |
[TestMethod] | |
public void UnequalGenericObjectsHaveDifferentHashCodes() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address2", "Austin", "TX"); | |
Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode()); | |
} | |
[TestMethod] | |
public void TransposedValuesOfFieldNamesGivesDifferentHashCodes() | |
{ | |
var address = new Address("_city", null, null); | |
var address2 = new Address(null, "_address1", null); | |
Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode()); | |
} | |
[TestMethod] | |
public void DerivedTypesHashCodesBehaveCorrectly() | |
{ | |
var address = new ExpandedAddress("Address99999", "Apt 123", "New Orleans", "LA"); | |
var address2 = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX"); | |
Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode()); | |
} | |
private class Address : GenericObject<Address> | |
{ | |
private readonly string _address1; | |
private readonly string _city; | |
private readonly string _state; | |
public Address(string address1, string city, string state) | |
{ | |
_address1 = address1; | |
_city = city; | |
_state = state; | |
} | |
public string Address1 | |
{ | |
get { return _address1; } | |
} | |
public string City | |
{ | |
get { return _city; } | |
} | |
public string State | |
{ | |
get { return _state; } | |
} | |
} | |
private class ExpandedAddress : Address | |
{ | |
private readonly string _address2; | |
public ExpandedAddress(string address1, string address2, string city, string state) | |
: base(address1, city, state) | |
{ | |
_address2 = address2; | |
} | |
public string Address2 | |
{ | |
get { return _address2; } | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Reflection; | |
namespace Project | |
{ | |
public class HeirarchicalEqualityComparer : IEqualityComparer | |
{ | |
public static readonly HeirarchicalEqualityComparer Default = new HeirarchicalEqualityComparer(); | |
public new bool Equals(object x, object y) | |
{ | |
if (ReferenceEquals(x, y)) | |
return true; | |
if (ReferenceEquals(x, null)) | |
return false; | |
if (ReferenceEquals(null, y)) | |
return false; | |
var t = x.GetType(); | |
if (t != y.GetType()) | |
return false; | |
foreach (var field in GetFields(t)) | |
{ | |
var value1 = field.GetValue(x); | |
var value2 = field.GetValue(y); | |
if (ReferenceEquals(value1, value2)) | |
continue; | |
if (ReferenceEquals(value1, null)) | |
return false; | |
if (ReferenceEquals(null, value2)) | |
return false; | |
if (!value1.Equals(value2)) | |
return false; | |
} | |
return true; | |
} | |
public int GetHashCode(object obj) | |
{ | |
if (obj == null) | |
throw new ArgumentNullException("obj"); | |
var t = obj.GetType(); | |
var hashCode = 0; | |
foreach (var field in GetFields(t)) | |
{ | |
var value = field.GetValue(obj); | |
hashCode = (hashCode*397) ^ (value == null ? 0 : value.GetHashCode()); | |
} | |
return hashCode; | |
} | |
private static IEnumerable<FieldInfo> GetFields(Type t) | |
{ | |
while (t != null && t != typeof (object) && t != typeof (ValueType)) | |
{ | |
foreach (var field in t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) | |
yield return field; | |
t = t.BaseType; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Project | |
{ | |
public abstract class HeirarchicalObject | |
{ | |
public override bool Equals(object obj) | |
{ | |
return HeirarchicalEqualityComparer.Default.Equals(this, obj); | |
} | |
public override int GetHashCode() | |
{ | |
return HeirarchicalEqualityComparer.Default.GetHashCode(this); | |
} | |
public static bool operator ==(HeirarchicalObject x, HeirarchicalObject y) | |
{ | |
return HeirarchicalEqualityComparer.Default.Equals(x, y); | |
} | |
public static bool operator !=(HeirarchicalObject x, HeirarchicalObject y) | |
{ | |
return !(x == y); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
namespace Project | |
{ | |
[TestClass] | |
public class HeirarchicalObjectTests | |
{ | |
[TestMethod] | |
public void AddressEqualsWorksWithIdenticalAddresses() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address1", "Austin", "TX"); | |
Assert.IsTrue(address.Equals(address2)); | |
} | |
[TestMethod] | |
public void AddressEqualsWorksWithNonIdenticalAddresses() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address2", "Austin", "TX"); | |
Assert.IsFalse(address.Equals(address2)); | |
} | |
[TestMethod] | |
public void AddressEqualsWorksWithNulls() | |
{ | |
var address = new Address(null, "Austin", "TX"); | |
var address2 = new Address("Address2", "Austin", "TX"); | |
Assert.IsFalse(address.Equals(address2)); | |
} | |
[TestMethod] | |
public void AddressEqualsWorksWithNullsOnOtherObject() | |
{ | |
var address = new Address("Address2", "Austin", "TX"); | |
var address2 = new Address("Address2", null, "TX"); | |
Assert.IsFalse(address.Equals(address2)); | |
} | |
[TestMethod] | |
public void AddressEqualsIsReflexive() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
Assert.IsTrue(address.Equals(address)); | |
} | |
[TestMethod] | |
public void AddressEqualsIsSymmetric() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address2", "Austin", "TX"); | |
Assert.IsFalse(address.Equals(address2)); | |
Assert.IsFalse(address2.Equals(address)); | |
} | |
[TestMethod] | |
public void AddressEqualsIsTransitive() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address1", "Austin", "TX"); | |
var address3 = new Address("Address1", "Austin", "TX"); | |
Assert.IsTrue(address.Equals(address2)); | |
Assert.IsTrue(address2.Equals(address3)); | |
Assert.IsTrue(address.Equals(address3)); | |
} | |
[TestMethod] | |
public void AddressOperatorsWork() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address1", "Austin", "TX"); | |
var address3 = new Address("Address2", "Austin", "TX"); | |
Assert.IsTrue(address == address2); | |
Assert.IsTrue(address2 != address3); | |
} | |
[TestMethod] | |
public void DerivedTypesBehaveCorrectly() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX"); | |
Assert.IsFalse(address.Equals(address2)); | |
Assert.IsFalse(address == address2); | |
} | |
[TestMethod] | |
public void DerivedTypesBehaveCorrectlyWhenFieldsAreSame() | |
{ | |
var address = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX"); | |
var address2 = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX"); | |
Assert.IsTrue(address.Equals(address2)); | |
Assert.IsTrue(address == address2); | |
} | |
[TestMethod] | |
public void DerivedTypesBehaveCorrectlyWhenNewFieldsAreDifferent() | |
{ | |
var address = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX"); | |
var address2 = new ExpandedAddress("Address1", "Apt 456", "Austin", "TX"); | |
Assert.IsFalse(address.Equals(address2)); | |
Assert.IsFalse(address == address2); | |
} | |
[TestMethod] | |
public void DerivedTypesBehaveCorrectlyWhenInheritedFieldsAreDifferent() | |
{ | |
var address = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX"); | |
var address2 = new ExpandedAddress("Address2", "Apt 123", "Las Vegas", "NV"); | |
Assert.IsFalse(address.Equals(address2)); | |
Assert.IsFalse(address == address2); | |
} | |
[TestMethod] | |
public void EqualValueObjectsHaveSameHashCode() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address1", "Austin", "TX"); | |
Assert.AreEqual(address.GetHashCode(), address2.GetHashCode()); | |
} | |
[TestMethod] | |
public void TransposedValuesGiveDifferentHashCodes() | |
{ | |
var address = new Address(null, "Austin", "TX"); | |
var address2 = new Address("TX", "Austin", null); | |
Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode()); | |
} | |
[TestMethod] | |
public void UnequalValueObjectsHaveDifferentHashCodes() | |
{ | |
var address = new Address("Address1", "Austin", "TX"); | |
var address2 = new Address("Address2", "Austin", "TX"); | |
Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode()); | |
} | |
[TestMethod] | |
public void TransposedValuesOfFieldNamesGivesDifferentHashCodes() | |
{ | |
var address = new Address("_city", null, null); | |
var address2 = new Address(null, "_address1", null); | |
Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode()); | |
} | |
[TestMethod] | |
public void DerivedTypesHashCodesBehaveCorrectly() | |
{ | |
var address = new ExpandedAddress("Address99999", "Apt 123", "New Orleans", "LA"); | |
var address2 = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX"); | |
Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode()); | |
} | |
private class Address : HeirarchicalObject | |
{ | |
private readonly string _address1; | |
private readonly string _city; | |
private readonly string _state; | |
public Address(string address1, string city, string state) | |
{ | |
_address1 = address1; | |
_city = city; | |
_state = state; | |
} | |
public string Address1 | |
{ | |
get { return _address1; } | |
} | |
public string City | |
{ | |
get { return _city; } | |
} | |
public string State | |
{ | |
get { return _state; } | |
} | |
} | |
private class ExpandedAddress : Address | |
{ | |
private readonly string _address2; | |
public ExpandedAddress(string address1, string address2, string city, string state) | |
: base(address1, city, state) | |
{ | |
_address2 = address2; | |
} | |
public string Address2 | |
{ | |
get { return _address2; } | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment