Skip to content

Instantly share code, notes, and snippets.

@agross
Created May 31, 2010 20:29
Show Gist options
  • Save agross/420249 to your computer and use it in GitHub Desktop.
Save agross/420249 to your computer and use it in GitHub Desktop.
using System;
namespace Crimson.NHibernateSupport
{
public interface IEntity
{
int Id
{
get;
}
}
public abstract class Entity : IEntity, IEquatable<Entity>
{
readonly int _id;
protected Entity()
{
}
protected Entity(int id)
{
_id = id;
}
public virtual int Id
{
get { return _id; }
}
public virtual bool Equals(Entity other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return Id.Equals(default(int)) ? base.Equals(other) : other.Id.Equals(Id);
}
public override bool Equals(object obj)
{
return Equals(obj as Entity);
}
public override int GetHashCode()
{
return Id.Equals(default(int)) ? base.GetHashCode() : Id.GetHashCode();
}
public static bool operator ==(Entity left, Entity right)
{
return Equals(left, right);
}
public static bool operator !=(Entity left, Entity right)
{
return !Equals(left, right);
}
}
}
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Crimson.NHibernateSupport
{
public abstract class ValueObject<T> : IEquatable<T> where T : ValueObject<T>
{
public virtual bool Equals(T other)
{
if (other == null)
{
return false;
}
Type leftType = GetType();
Type otherType = other.GetType();
if (leftType != otherType)
{
return false;
}
IEnumerable<FieldInfo> fields = GetFieldsFromTypeHierarchy();
foreach (FieldInfo field in fields)
{
object value1 = field.GetValue(this);
object value2 = field.GetValue(other);
if (value1 == null)
{
if (value2 != null)
{
return false;
}
}
else if (!value1.Equals(value2))
{
return false;
}
}
return true;
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
return Equals(obj as T);
}
public override int GetHashCode()
{
IEnumerable<FieldInfo> fields = GetFieldsFromTypeHierarchy();
const int StartValue = 17;
const int Multiplier = 59;
int hashCode = StartValue;
foreach (FieldInfo field in fields)
{
object value = field.GetValue(this);
hashCode = (hashCode * Multiplier) + (value != null ? value.GetHashCode() : StartValue);
}
return hashCode;
}
IEnumerable<FieldInfo> GetFieldsFromTypeHierarchy()
{
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> left, ValueObject<T> right)
{
return Equals(left, right);
}
public static bool operator !=(ValueObject<T> left, ValueObject<T> right)
{
return !Equals(left, right);
}
}
}
using System;
using Machine.Specifications;
namespace Crimson.NHibernateSupport.Tests
{
public class Person : Entity
{
public Person(string name)
{
Name = name;
}
public Person(int id, string name) : base(id)
{
Name = name;
}
public string Name
{
get;
private set;
}
}
[Behaviors]
internal class EntityInequality
{
protected static Person Person1;
protected static Person Person2;
It should_be_considered_as_unequal = () => ((Object) Person1).Equals(Person2).ShouldBeFalse();
It should_be_considered_as_unequal_with_equality_operator = () => (Person1 == Person2).ShouldBeFalse();
It should_be_considered_as_unequal_with_inequality_operator = () => (Person1 != Person2).ShouldBeTrue();
It should_have_symmetric_inequality = () => ((Object) Person2).Equals(Person1).ShouldBeFalse();
It should_compute_a_different_hash = () => Person1.GetHashCode().ShouldNotEqual(Person2.GetHashCode());
}
[Behaviors]
internal class EntityEquality
{
protected static Person Person1;
protected static Person Person2;
It should_be_considered_as_equal = () => ((Object) Person1).Equals(Person2).ShouldBeTrue();
It should_be_considered_as_equal_with_equality_operator = () => (Person1 == Person2).ShouldBeTrue();
It should_be_considered_as_equal_with_inequality_operator = () => (Person1 != Person2).ShouldBeFalse();
It should_have_symmetric_inequality = () => ((Object) Person2).Equals(Person1).ShouldBeTrue();
It should_compute_the_same_hash = () => Person1.GetHashCode().ShouldEqual(Person2.GetHashCode());
}
[Subject(typeof(Entity))]
public class When_comparing_unsaved_entities_with_identical_values
{
protected static Person Person1;
protected static Person Person2;
Establish context = () =>
{
Person1 = new Person("Darth Vader");
Person2 = new Person("Darth Vader");
};
Behaves_like<EntityInequality> inequal_entities;
}
[Subject(typeof(Entity))]
public class When_comparing_unsaved_entities_with_different_values
{
protected static Person Person1;
protected static Person Person2;
Establish context = () =>
{
Person1 = new Person("Darth Vader");
Person2 = new Person("Senator Palpatine");
};
Behaves_like<EntityInequality> inequal_entities;
}
[Subject(typeof(Entity))]
public class When_comparing_saved_entities_with_different_IDs
{
protected static Person Person1;
protected static Person Person2;
Establish context = () =>
{
Person1 = new Person(42, "Darth Vader");
Person2 = new Person(43, "Senator Palpatine");
};
Behaves_like<EntityInequality> inequal_entities;
}
[Subject(typeof(Entity))]
public class When_comparing_saved_entities_with_identical_IDs
{
protected static Person Person1;
protected static Person Person2;
Establish context = () =>
{
Person1 = new Person(42, "Darth Vader");
Person2 = new Person(42, "Senator Palpatine");
};
Behaves_like<EntityEquality> equal_entities;
}
[Subject(typeof(Entity))]
public class When_comparing_the_same_entity_with_itself
{
static Person Person;
Establish context = () => { Person = new Person("Darth Vader"); };
It should_have_reflexive_equality = () => Person.Equals(Person).ShouldBeTrue();
}
}
using System;
using Machine.Specifications;
namespace Crimson.NHibernateSupport.Tests
{
#region Test Types
public class StreetAddress : ValueObject<StreetAddress>
{
readonly string _city;
readonly string _state;
readonly string _street;
public StreetAddress(string street, string city, string state)
{
_street = street;
_city = city;
_state = state;
}
public string Street
{
get { return _street; }
}
public string City
{
get { return _city; }
}
public string State
{
get { return _state; }
}
}
public class StreetAddressWithStreetNumber : StreetAddress
{
readonly string _streetNumber;
public StreetAddressWithStreetNumber(string street, string streetNumber, string city, string state)
: base(street, city, state)
{
_streetNumber = streetNumber;
}
public string StreetNumber
{
get { return _streetNumber; }
}
}
#endregion
[Behaviors]
internal class ValueObjectInequality
{
protected static StreetAddress Address1;
protected static StreetAddress Address2;
It should_be_considered_as_unequal = () => ((Object) Address1).Equals(Address2).ShouldBeFalse();
It should_be_considered_as_unequal_with_equality_operator = () => (Address1 == Address2).ShouldBeFalse();
It should_be_considered_as_unequal_with_inequality_operator = () => (Address1 != Address2).ShouldBeTrue();
It should_have_symmetric_unequality = () => ((Object) Address2).Equals(Address1).ShouldBeFalse();
It should_compute_a_different_hash = () => Address1.GetHashCode().ShouldNotEqual(Address2.GetHashCode());
}
[Behaviors]
internal class ValueObjectEquality
{
protected static StreetAddress Address1;
protected static StreetAddress Address2;
It should_be_considered_as_equal = () => ((Object) Address1).Equals(Address2).ShouldBeTrue();
It should_be_considered_as_equal_with_equality_operator = () => (Address1 == Address2).ShouldBeTrue();
It should_be_considered_as_equal_with_inequality_operator = () => (Address1 != Address2).ShouldBeFalse();
It should_have_symmetric_equality = () => ((Object) Address2).Equals(Address1).ShouldBeTrue();
It should_compute_the_same_hash = () => Address1.GetHashCode().ShouldEqual(Address2.GetHashCode());
}
[Subject(typeof(ValueObject<>))]
public class When_comparing_value_objects_with_identical_values
{
protected static StreetAddress Address1;
protected static StreetAddress Address2;
Establish context = () =>
{
Address1 = new StreetAddress("Street", "Austin", "TX");
Address2 = new StreetAddress("Street", "Austin", "TX");
};
Behaves_like<ValueObjectEquality> equal_value_objects;
}
[Subject(typeof(ValueObject<>))]
public class When_comparing_value_objects_with_different_values
{
protected static StreetAddress Address1;
protected static StreetAddress Address2;
Establish context = () =>
{
Address1 = new StreetAddress("Street", "Austin", "TX");
Address2 = new StreetAddress("Foo", "Austin", "TX");
};
Behaves_like<ValueObjectInequality> unequal_value_objects;
}
[Subject(typeof(ValueObject<>))]
public class When_comparing_different_value_objects_with_null_values_on_one_object
{
protected static StreetAddress Address1;
protected static StreetAddress Address2;
Establish context = () =>
{
Address1 = new StreetAddress(null, "Austin", "TX");
Address2 = new StreetAddress("Street", "Austin", "TX");
};
Behaves_like<ValueObjectInequality> unequal_value_objects;
}
[Subject(typeof(ValueObject<>))]
public class When_comparing_a_value_object_against_null
{
static StreetAddress StreetAddress;
Establish context = () => { StreetAddress = new StreetAddress("Street", "Austin", "TX"); };
It should_be_considered_as_unequal = () => StreetAddress.Equals(null).ShouldBeFalse();
}
[Subject(typeof(ValueObject<>))]
public class When_comparing_the_same_value_object_with_itself
{
static StreetAddress StreetAddress;
Establish context = () => { StreetAddress = new StreetAddress("StreetAddress", "Austin", "TX"); };
It should_have_reflexive_equality = () => StreetAddress.Equals(StreetAddress).ShouldBeTrue();
}
[Subject(typeof(ValueObject<>))]
public class When_comparing_three_value_objects_with_identical_values
{
static StreetAddress Address1;
static StreetAddress Address2;
static StreetAddress Address3;
Establish context = () =>
{
Address1 = new StreetAddress("StreetAddress", "Austin", "TX");
Address2 = new StreetAddress("StreetAddress", "Austin", "TX");
Address3 = new StreetAddress("StreetAddress", "Austin", "TX");
};
It should_have_transitive_equality_for_A_and_B = () => ((Object) Address1).Equals(Address2).ShouldBeTrue();
It should_have_transitive_equality_for_B_and_C = () => ((Object) Address2).Equals(Address3).ShouldBeTrue();
It should_have_transitive_equality_for_A_and_C = () => ((Object) Address1).Equals(Address3).ShouldBeTrue();
}
[Subject(typeof(ValueObject<>))]
public class When_comparing_value_objects_with_transposed_field_values
{
protected static StreetAddress Address1;
protected static StreetAddress Address2;
Establish context = () =>
{
Address1 = new StreetAddress(null, "Austin", "TX");
Address2 = new StreetAddress("TX", "Austin", null);
};
Behaves_like<ValueObjectInequality> unequal_value_objects;
}
[Subject(typeof(ValueObject<>))]
public class When_comparing_a_value_object_against_a_derived_one_with_the_same_values
{
protected static StreetAddress Address1;
protected static StreetAddressWithStreetNumber Address2;
Establish context = () =>
{
Address1 = new StreetAddress("Street", "Austin", "TX");
Address2 = new StreetAddressWithStreetNumber("Street", "StreetNumber", "Austin", "TX");
};
Behaves_like<ValueObjectInequality> unequal_value_objects;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment