U2U.ValueObjectComparers 0.1.0

Implement a ValueObject Equals method efficiently.

There is a newer version of this package available.
See the version list below for details.
Install-Package U2U.ValueObjectComparers -Version 0.1.0
dotnet add package U2U.ValueObjectComparers --version 0.1.0
<PackageReference Include="U2U.ValueObjectComparers" Version="0.1.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add U2U.ValueObjectComparers --version 0.1.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.

Implementing ValueObject's Equality - Efficiently

Equality

This blog discusses how to implement a Value Object's Equals method efficiently.

What are Value Objects?

In Domain Driven Design objects are divided into two groups: Entities and Value Objects.

Entities are objects that have an identity and life cycle.

Value Objects are objects that don't have any real identity and are mainly used to describe aspects of an entity, such as your name which is of type string.
String is a value object, because with value objects you don't care which instance you are holding.
For example, when writing one a whiteboard you want to use a blue marker. If you have many markers which are blue, do you care which one you are holding?
If so, then that marker is an entity, if not it is a value object. Entities are equal when they have the same identity, value objects are equal when all properties that define one are equal.

Implementing Equality for Value Objects

To implement equality for a value object we need to compare each of its properties for equality (You could say that a value object's identity is defined by all of its properties). This is not hard, but it is repetitive work.
Each time you add a new property you have to update the Equals method to use that property too.

The Microsoft Approach

There are implementations for a ValueObject base class, which takes care of most of the work, for example, the one from Microsoft Docs.

Here you need to override the GetAtomicValues method.

In case of Address (copied from Docs):

protected override IEnumerable<object> GetAtomicValues()
{
  // Using a yield return statement to return each element one at a time
  yield return Street;
  yield return City;
  yield return State;
  yield return Country;
  yield return ZipCode;
}

Pretty simple, but I have two problems with this.

First of all, you need to inherit from the ValueObject base class.
This excludes the use of Value Types as a Value Object.
Value types (struct) are ideal Value Objects because they get embedded in the entities, just like built-in value objects int and others.

The second objection is that you should not forget to add an extra property to this method each time you add a property to the type...

Using Reflection

So what is the solution? Of course, you could use reflection to implement Equals like here.
In this case, reflection automatically discovers all the properties and compares all of them, returning true is all properties are equal.

The problem with reflection is that it is slow, so you should limit reflection to "just once".

/slow.png

"Just Once" Reflection

There is a third approach where you use reflection to figure out what to do and generate the code so the second time things go fast.
That is the approach I took to build ValueObjectComparer&lt;T&gt;.

Here is an example of what a Value Object looks like.
The Equals method simply delegates to the ValueObjectComparer&lt;SomeObject&gt;.
Same for the IEquatable&lt;SomeObject&gt; interface implementation.

public class SomeObject : IEquatable<SomeObject>
{
  public string Name { get; set; }
  public int Age { get; set; }

  public override bool Equals(object obj)
    => ValueObjectComparer<SomeObject>.Instance.Equals(this, obj);

  public bool Equals([AllowNull] SomeObject other) 
    => ValueObjectComparer<SomeObject>.Instance.Equals(this, other);
}

Performance

Let's see how the performance compares between the 'Equals' as prescribed by Microsoft or the "Just Once" reflection implementation.
For this I have used the excellent BenchmarkDotNet library, and here are the results:

|                                     Method |      Mean |       Min |       Max |
|------------------------------------------- |----------:|----------:|----------:|
|            UsingMyValueObjectsThatAreEqual |  32.44 ms |  31.30 ms |  34.63 ms |
| UsingMyValueObjectsThatAreEqualWithNesting |  93.92 ms |  91.64 ms |  96.76 ms |
|         UsingMyValueObjectsThatAreNotEqual |  29.57 ms |  28.89 ms |  30.45 ms |
|            UsingMSValueObjectsThatAreEqual | 296.74 ms | 293.22 ms | 303.94 ms |
| UsingMSValueObjectsThatAreEqualWithNesting | 625.80 ms | 597.04 ms | 669.63 ms |
|         UsingMSValueObjectsThatAreNotEqual | 248.53 ms | 238.87 ms | 261.64 ms |
|      UsingMyValueObjectStructsThatAreEqual |  91.75 ms |  87.91 ms | 103.14 ms |

Using ValueObjectComparer&lt;T&gt; results in about a 10x faster implementation.

Speedy

Implementing ValueObject's Equality - Efficiently

Equality

This blog discusses how to implement a Value Object's Equals method efficiently.

What are Value Objects?

In Domain Driven Design objects are divided into two groups: Entities and Value Objects.

Entities are objects that have an identity and life cycle.

Value Objects are objects that don't have any real identity and are mainly used to describe aspects of an entity, such as your name which is of type string.
String is a value object, because with value objects you don't care which instance you are holding.
For example, when writing one a whiteboard you want to use a blue marker. If you have many markers which are blue, do you care which one you are holding?
If so, then that marker is an entity, if not it is a value object. Entities are equal when they have the same identity, value objects are equal when all properties that define one are equal.

Implementing Equality for Value Objects

To implement equality for a value object we need to compare each of its properties for equality (You could say that a value object's identity is defined by all of its properties). This is not hard, but it is repetitive work.
Each time you add a new property you have to update the Equals method to use that property too.

The Microsoft Approach

There are implementations for a ValueObject base class, which takes care of most of the work, for example, the one from Microsoft Docs.

Here you need to override the GetAtomicValues method.

In case of Address (copied from Docs):

protected override IEnumerable<object> GetAtomicValues()
{
  // Using a yield return statement to return each element one at a time
  yield return Street;
  yield return City;
  yield return State;
  yield return Country;
  yield return ZipCode;
}

Pretty simple, but I have two problems with this.

First of all, you need to inherit from the ValueObject base class.
This excludes the use of Value Types as a Value Object.
Value types (struct) are ideal Value Objects because they get embedded in the entities, just like built-in value objects int and others.

The second objection is that you should not forget to add an extra property to this method each time you add a property to the type...

Using Reflection

So what is the solution? Of course, you could use reflection to implement Equals like here.
In this case, reflection automatically discovers all the properties and compares all of them, returning true is all properties are equal.

The problem with reflection is that it is slow, so you should limit reflection to "just once".

/slow.png

"Just Once" Reflection

There is a third approach where you use reflection to figure out what to do and generate the code so the second time things go fast.
That is the approach I took to build ValueObjectComparer&lt;T&gt;.

Here is an example of what a Value Object looks like.
The Equals method simply delegates to the ValueObjectComparer&lt;SomeObject&gt;.
Same for the IEquatable&lt;SomeObject&gt; interface implementation.

public class SomeObject : IEquatable<SomeObject>
{
  public string Name { get; set; }
  public int Age { get; set; }

  public override bool Equals(object obj)
    => ValueObjectComparer<SomeObject>.Instance.Equals(this, obj);

  public bool Equals([AllowNull] SomeObject other) 
    => ValueObjectComparer<SomeObject>.Instance.Equals(this, other);
}

Performance

Let's see how the performance compares between the 'Equals' as prescribed by Microsoft or the "Just Once" reflection implementation.
For this I have used the excellent BenchmarkDotNet library, and here are the results:

|                                     Method |      Mean |       Min |       Max |
|------------------------------------------- |----------:|----------:|----------:|
|            UsingMyValueObjectsThatAreEqual |  32.44 ms |  31.30 ms |  34.63 ms |
| UsingMyValueObjectsThatAreEqualWithNesting |  93.92 ms |  91.64 ms |  96.76 ms |
|         UsingMyValueObjectsThatAreNotEqual |  29.57 ms |  28.89 ms |  30.45 ms |
|            UsingMSValueObjectsThatAreEqual | 296.74 ms | 293.22 ms | 303.94 ms |
| UsingMSValueObjectsThatAreEqualWithNesting | 625.80 ms | 597.04 ms | 669.63 ms |
|         UsingMSValueObjectsThatAreNotEqual | 248.53 ms | 238.87 ms | 261.64 ms |
|      UsingMyValueObjectStructsThatAreEqual |  91.75 ms |  87.91 ms | 103.14 ms |

Using ValueObjectComparer&lt;T&gt; results in about a 10x faster implementation.

Speedy

Release Notes

Use at your own risk ;)

  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version History

Version Downloads Last updated
2.2.0 101 4/7/2020
0.2.0 268 12/31/2019
0.1.1 158 12/30/2019
0.1.0 111 12/26/2019