This post aims to provide a way to implement a base class for NHibernate components also known as Value Objects in Domain-driven design.

In my previous post I discussed about the case where you want to map a component with NHibernate and introduced the ComponentBase(Of T) class. However, to make it straightforward that you need to override Equals (you also need to override GetHashCode) in your derived classes, I modified the ComponentBase(Of T) class to implement the IEquatable(Of T) interface. Furthermore, since NHibernate works only with reference types (that is, a class) I also constrained it to accept only reference types.
Here is the Component(Of T) class:
using System;
public abstract class ComponentBase<T>
: IEquatable<T> where T : class
{
/// <summary>
/// Indicates whether the current object is equal to another
/// object of the same type.
/// </summary>
/// <param name="other">An object to compare with this object.
/// </param>
/// <returns>true if the current object is equal to the other
/// parameter; otherwise, false.</returns>
public abstract bool Equals(T other);
/// <summary>
/// Serves as a hash function for a particular type,
/// suitable for use in hashing
/// algorithms and data structures such as a hash table.
/// </summary>
/// <returns>
/// A hash code for this instance of the type.
/// </returns>
public abstract int GetHashCodeForType();
/// <summary>
/// Determines whether the specified <see cref="System.Object"/>
/// is equal to this instance.
/// </summary>
/// <param name="obj">The <see cref="System.Object"/> to
/// compare with this instance.
/// </param>
/// <returns>
/// <c>true</c> if the specified <see cref="System.Object"/>
/// is equal to this instance;
/// otherwise, <c>false</c>.
/// </returns>
public sealed override bool Equals(object obj)
{
// The given object to compare to can't be null.
if (obj == null) { return false; }
// If objects are different types, they can't be equal.
if (this.GetType() != obj.GetType()) { return false; }
return Equals(obj as T);
}
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for
/// use in hashing algorithms and data structures
/// like a hash table.
/// </returns>
public sealed override int GetHashCode()
{
return GetHashCodeForType();
}
}
Below are some test cases:
using System;
using Xunit;
public sealed class ComponentBaseTest
{
private sealed class MockType
: ComponentBase<MockType> { /* ... */ }
[Fact]
public void TheSameInstanceHasTheSameHashCode()
{
MockType mt1 = new MockType(5);
MockType mt2 = mt1;
Assert.Equal(mt1.GetHashCode(), mt2.GetHashCode());
}
[Fact]
public void DiffInstancesWithSameCtorParamsHaveTheSameHashCode()
{
MockType mt1 = new MockType(5);
MockType mt3 = new MockType(5);
Assert.Equal(mt1.GetHashCode(), mt3.GetHashCode());
}
[Fact]
public void TestDiffInstancesWithSameCtorParamsAreEqual()
{
MockType mt1 = new MockType(5);
MockType mt3 = new MockType(5);
// Objects are equal.
Assert.True(mt1.Equals(mt3));
// References are not equal.
Assert.False(object.ReferenceEquals(mt1, mt3));
}
[Fact]
public void TestDiffInstancesWithDiffCtorParamsAreNotEqual()
{
MockType mt1 = new MockType(1);
MockType mt3 = new MockType(3);
// Objects are not equal.
Assert.False(mt1.Equals(mt3));
// References are not equal.
Assert.False(object.ReferenceEquals(mt1, mt3));
}
}
You can use this class in your component collections. If you want to map a Set (that is, an unordered collection of unique entities where duplicates are not allowed) just derive your component types from ComponentBase(Of T). Derived types need to implement the strongly-typed version of Equals and also the GetHashCode methods.