|
|
|
@ -1,98 +1,61 @@
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
|
|
|
|
|
namespace Volo.Abp.Domain.Values
|
|
|
|
|
{
|
|
|
|
|
//Inspired from https://blogs.msdn.microsoft.com/cesardelatorre/2011/06/06/implementing-a-value-object-base-class-supertype-patternddd-patterns-related/
|
|
|
|
|
//Inspired from https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/microservice-ddd-cqrs-patterns/implement-value-objects
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Base class for value objects.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="TValueObject">The type of the value object.</typeparam>
|
|
|
|
|
public abstract class ValueObject<TValueObject> : IEquatable<TValueObject>
|
|
|
|
|
where TValueObject : ValueObject<TValueObject>
|
|
|
|
|
public abstract class ValueObject
|
|
|
|
|
{
|
|
|
|
|
public bool Equals(TValueObject other)
|
|
|
|
|
protected static bool EqualOperator(ValueObject left, ValueObject right)
|
|
|
|
|
{
|
|
|
|
|
if ((object)other == null)
|
|
|
|
|
if (ReferenceEquals(left, null) ^ ReferenceEquals(right, null))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return ReferenceEquals(left, null) || left.Equals(right);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var publicProperties = GetType().GetTypeInfo().GetProperties();
|
|
|
|
|
if (!publicProperties.Any())
|
|
|
|
|
protected static bool NotEqualOperator(ValueObject left, ValueObject right)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
return !(EqualOperator(left, right));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return publicProperties.All(property => Equals(property.GetValue(this, null), property.GetValue(other, null)));
|
|
|
|
|
}
|
|
|
|
|
protected abstract IEnumerable<object> GetAtomicValues();
|
|
|
|
|
|
|
|
|
|
public override bool Equals(object obj)
|
|
|
|
|
{
|
|
|
|
|
if (obj == null)
|
|
|
|
|
if (obj == null || obj.GetType() != GetType())
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var item = obj as ValueObject<TValueObject>;
|
|
|
|
|
return (object)item != null && Equals((TValueObject)item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override int GetHashCode()
|
|
|
|
|
{
|
|
|
|
|
//TODO: Can we cache the hash value assuming value objects are always immutable? We can make a Reset-like method to reset it's mutated.
|
|
|
|
|
|
|
|
|
|
const int index = 1;
|
|
|
|
|
const int initialHasCode = 31;
|
|
|
|
|
|
|
|
|
|
var publicProperties = GetType().GetTypeInfo().GetProperties();
|
|
|
|
|
|
|
|
|
|
if (!publicProperties.Any())
|
|
|
|
|
{
|
|
|
|
|
return initialHasCode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var hashCode = initialHasCode;
|
|
|
|
|
var changeMultiplier = false;
|
|
|
|
|
|
|
|
|
|
foreach (var property in publicProperties)
|
|
|
|
|
{
|
|
|
|
|
var value = property.GetValue(this, null);
|
|
|
|
|
|
|
|
|
|
if (value == null)
|
|
|
|
|
{
|
|
|
|
|
//support {"a",null,null,"a"} != {null,"a","a",null}
|
|
|
|
|
hashCode = hashCode ^ (index * 13);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hashCode = hashCode * (changeMultiplier ? 59 : 114) + value.GetHashCode();
|
|
|
|
|
changeMultiplier = !changeMultiplier;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return hashCode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool operator ==(ValueObject<TValueObject> x, ValueObject<TValueObject> y)
|
|
|
|
|
ValueObject other = (ValueObject)obj;
|
|
|
|
|
IEnumerator<object> thisValues = GetAtomicValues().GetEnumerator();
|
|
|
|
|
IEnumerator<object> otherValues = other.GetAtomicValues().GetEnumerator();
|
|
|
|
|
while (thisValues.MoveNext() && otherValues.MoveNext())
|
|
|
|
|
{
|
|
|
|
|
if (ReferenceEquals(x, y))
|
|
|
|
|
if (ReferenceEquals(thisValues.Current, null) ^
|
|
|
|
|
ReferenceEquals(otherValues.Current, null))
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (((object)x == null) || ((object)y == null))
|
|
|
|
|
if (thisValues.Current != null &&
|
|
|
|
|
!thisValues.Current.Equals(otherValues.Current))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return x.Equals(y);
|
|
|
|
|
}
|
|
|
|
|
return !thisValues.MoveNext() && !otherValues.MoveNext();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool operator !=(ValueObject<TValueObject> x, ValueObject<TValueObject> y)
|
|
|
|
|
public override int GetHashCode()
|
|
|
|
|
{
|
|
|
|
|
return !(x == y);
|
|
|
|
|
return GetAtomicValues()
|
|
|
|
|
.Select(x => x != null ? x.GetHashCode() : 0)
|
|
|
|
|
.Aggregate((x, y) => x ^ y);
|
|
|
|
|
}
|
|
|
|
|
// Other utilility methods
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|