Skip to content
This repository was archived by the owner on Nov 17, 2023. It is now read-only.

Commit 921de69

Browse files
refactored Equals() method on ValueObject (#1316)
1 parent b426909 commit 921de69

File tree

3 files changed

+197
-18
lines changed

3 files changed

+197
-18
lines changed

src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Address.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public Address(string street, string city, string state, string country, string
2323
ZipCode = zipcode;
2424
}
2525

26-
protected override IEnumerable<object> GetAtomicValues()
26+
protected override IEnumerable<object> GetEqualityComponents()
2727
{
2828
// Using a yield return statement to return each element one at a time
2929
yield return Street;

src/Services/Ordering/Ordering.Domain/SeedWork/ValueObject.cs

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,34 +19,23 @@ protected static bool NotEqualOperator(ValueObject left, ValueObject right)
1919
return !(EqualOperator(left, right));
2020
}
2121

22-
protected abstract IEnumerable<object> GetAtomicValues();
22+
protected abstract IEnumerable<object> GetEqualityComponents();
2323

2424
public override bool Equals(object obj)
2525
{
2626
if (obj == null || obj.GetType() != GetType())
2727
{
2828
return false;
2929
}
30-
ValueObject other = (ValueObject)obj;
31-
IEnumerator<object> thisValues = GetAtomicValues().GetEnumerator();
32-
IEnumerator<object> otherValues = other.GetAtomicValues().GetEnumerator();
33-
while (thisValues.MoveNext() && otherValues.MoveNext())
34-
{
35-
if (ReferenceEquals(thisValues.Current, null) ^ ReferenceEquals(otherValues.Current, null))
36-
{
37-
return false;
38-
}
39-
if (thisValues.Current != null && !thisValues.Current.Equals(otherValues.Current))
40-
{
41-
return false;
42-
}
43-
}
44-
return !thisValues.MoveNext() && !otherValues.MoveNext();
30+
31+
var other = (ValueObject)obj;
32+
33+
return this.GetEqualityComponents().SequenceEqual(other.GetEqualityComponents());
4534
}
4635

4736
public override int GetHashCode()
4837
{
49-
return GetAtomicValues()
38+
return GetEqualityComponents()
5039
.Select(x => x != null ? x.GetHashCode() : 0)
5140
.Aggregate((x, y) => x ^ y);
5241
}
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using Xunit;
7+
8+
namespace Ordering.UnitTests.Domain.SeedWork
9+
{
10+
public class ValueObjectTests
11+
{
12+
public ValueObjectTests()
13+
{ }
14+
15+
[Theory]
16+
[MemberData(nameof(EqualValueObjects))]
17+
public void Equals_EqualValueObjects_ReturnsTrue(ValueObject instanceA, ValueObject instanceB, string reason)
18+
{
19+
// Act
20+
var result = EqualityComparer<ValueObject>.Default.Equals(instanceA, instanceB);
21+
22+
// Assert
23+
Assert.True(result, reason);
24+
}
25+
26+
[Theory]
27+
[MemberData(nameof(NonEqualValueObjects))]
28+
public void Equals_NonEqualValueObjects_ReturnsFalse(ValueObject instanceA, ValueObject instanceB, string reason)
29+
{
30+
// Act
31+
var result = EqualityComparer<ValueObject>.Default.Equals(instanceA, instanceB);
32+
33+
// Assert
34+
Assert.False(result, reason);
35+
}
36+
37+
private static readonly ValueObject APrettyValueObject = new ValueObjectA(1, "2", Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), new ComplexObject(2, "3"));
38+
39+
public static readonly TheoryData<ValueObject, ValueObject, string> EqualValueObjects = new TheoryData<ValueObject, ValueObject, string>
40+
{
41+
{
42+
null,
43+
null,
44+
"they should be equal because they are both null"
45+
},
46+
{
47+
APrettyValueObject,
48+
APrettyValueObject,
49+
"they should be equal because they are the same object"
50+
},
51+
{
52+
new ValueObjectA(1, "2", Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), new ComplexObject(2, "3")),
53+
new ValueObjectA(1, "2", Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), new ComplexObject(2, "3")),
54+
"they should be equal because they have equal members"
55+
},
56+
{
57+
new ValueObjectA(1, "2", Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), new ComplexObject(2, "3"), notAnEqualityComponent: "xpto"),
58+
new ValueObjectA(1, "2", Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), new ComplexObject(2, "3"), notAnEqualityComponent: "xpto2"),
59+
"they should be equal because all equality components are equal, even though an additional member was set"
60+
},
61+
{
62+
new ValueObjectB(1, "2", 1, 2, 3 ),
63+
new ValueObjectB(1, "2", 1, 2, 3 ),
64+
"they should be equal because all equality components are equal, including the 'C' list"
65+
}
66+
};
67+
68+
public static readonly TheoryData<ValueObject, ValueObject, string> NonEqualValueObjects = new TheoryData<ValueObject, ValueObject, string>
69+
{
70+
{
71+
new ValueObjectA(a: 1, b: "2", c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(2, "3")),
72+
new ValueObjectA(a: 2, b: "2", c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(2, "3")),
73+
"they should not be equal because the 'A' member on ValueObjectA is different among them"
74+
},
75+
{
76+
new ValueObjectA(a: 1, b: "2", c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(2, "3")),
77+
new ValueObjectA(a: 1, b: null, c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(2, "3")),
78+
"they should not be equal because the 'B' member on ValueObjectA is different among them"
79+
},
80+
{
81+
new ValueObjectA(a: 1, b: "2", c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(a: 2, b: "3")),
82+
new ValueObjectA(a: 1, b: "2", c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(a: 3, b: "3")),
83+
"they should not be equal because the 'A' member on ValueObjectA's 'D' member is different among them"
84+
},
85+
{
86+
new ValueObjectA(a: 1, b: "2", c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(a: 2, b: "3")),
87+
new ValueObjectB(a: 1, b: "2"),
88+
"they should not be equal because they are not of the same type"
89+
},
90+
{
91+
new ValueObjectB(1, "2", 1, 2, 3 ),
92+
new ValueObjectB(1, "2", 1, 2, 3, 4 ),
93+
"they should be not be equal because the 'C' list contains one additional value"
94+
},
95+
{
96+
new ValueObjectB(1, "2", 1, 2, 3, 5 ),
97+
new ValueObjectB(1, "2", 1, 2, 3 ),
98+
"they should be not be equal because the 'C' list contains one additional value"
99+
},
100+
{
101+
new ValueObjectB(1, "2", 1, 2, 3, 5 ),
102+
new ValueObjectB(1, "2", 1, 2, 3, 4 ),
103+
"they should be not be equal because the 'C' lists are not equal"
104+
}
105+
106+
};
107+
108+
private class ValueObjectA : ValueObject
109+
{
110+
public ValueObjectA(int a, string b, Guid c, ComplexObject d, string notAnEqualityComponent = null)
111+
{
112+
A = a;
113+
B = b;
114+
C = c;
115+
D = d;
116+
NotAnEqualityComponent = notAnEqualityComponent;
117+
}
118+
119+
public int A { get; }
120+
public string B { get; }
121+
public Guid C { get; }
122+
public ComplexObject D { get; }
123+
public string NotAnEqualityComponent { get; }
124+
125+
protected override IEnumerable<object> GetEqualityComponents()
126+
{
127+
yield return A;
128+
yield return B;
129+
yield return C;
130+
yield return D;
131+
}
132+
}
133+
134+
private class ValueObjectB : ValueObject
135+
{
136+
public ValueObjectB(int a, string b, params int[] c)
137+
{
138+
A = a;
139+
B = b;
140+
C = c.ToList();
141+
}
142+
143+
public int A { get; }
144+
public string B { get; }
145+
146+
public List<int> C { get; }
147+
148+
protected override IEnumerable<object> GetEqualityComponents()
149+
{
150+
yield return A;
151+
yield return B;
152+
153+
foreach (var c in C)
154+
{
155+
yield return c;
156+
}
157+
}
158+
}
159+
160+
private class ComplexObject : IEquatable<ComplexObject>
161+
{
162+
public ComplexObject(int a, string b)
163+
{
164+
A = a;
165+
B = b;
166+
}
167+
168+
public int A { get; set; }
169+
170+
public string B { get; set; }
171+
172+
public override bool Equals(object obj)
173+
{
174+
return Equals(obj as ComplexObject);
175+
}
176+
177+
public bool Equals(ComplexObject other)
178+
{
179+
return other != null &&
180+
A == other.A &&
181+
B == other.B;
182+
}
183+
184+
public override int GetHashCode()
185+
{
186+
return HashCode.Combine(A, B);
187+
}
188+
}
189+
}
190+
}

0 commit comments

Comments
 (0)