public abstract class ValueObject : IEquatable<ValueObject>
{
public abstract Validation Validate();
public abstract bool Equals(ValueObject other);
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
// If the given object is of the same type, but not the same ValueObject, return false.
if (GetType() != obj.GetType())
{
return false;
}
// We can safely cast the given object to the correct ValueObject type
ValueObject other = (ValueObject) obj;
// Return true if the ValueObjects are equal (by implementing Equals)
return Equals(other);
}
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = (int) 2166136261;
// Suitable nullity checks etc, of course :)
hash = (hash * 16777619) ^ Value.GetHashCode();
hash = (hash * 16777619) ^ GetType().GetHashCode();
hash = (hash * 16777619) ^ EqualityComparer.Default.GetHashCode();
return hash;
}
}
public static bool operator ==(ValueObject left, ValueObject right)
{
if (ReferenceEquals(left, null))
{
return ReferenceEquals(right, null);
}
return left.Equals(right);
}
public static bool operator !=(ValueObject left, ValueObject right)
{
return !(left == right);
}
// this allows value objects to be compared against the primitive value.
public static bool operator ==(ValueObject left, object right)
{
if (ReferenceEquals(left, null))
{
return ReferenceEquals(right, null);
}
return left.Equals(right);
}
public static bool operator !=(ValueObject left, object right)
{
return !(left == right);
}
protected bool Equals(ValueObject other)
{
if (other == null)
{
return false;
}
if (GetType() != other.GetType())
{
return false;
}
// Compare the values of the ValueObjects (in a subclass)
return Value.Equals(other.Value);
}
public override string ToString()
{
return Value.ToString();
}
// this lets you compare against primitive types
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (GetType() != obj.GetType())
{
return false;
}
ValueObject other = (ValueObject) obj;
return Equals(other);
}
// this is the value the value object holds (in subclasses)
public abstract object Value { get; }
}
``` cs
public class CustomerId : ValueObject
{
public CustomerId(int value)
{
Value = value;
}
public static CustomerId From(int value)
{
return new CustomerId(value);
}
public override Validation Validate()
{
if (Value <= 0)
{
return Validation.Invalid("Customer IDs cannot be zero or negative.");
}
return Validation.Ok;
}
public override object Value { get; }
}
``` cs
public class SupplierId : ValueObject
{
public SupplierId(int value)
{
Value = value;
}
public static SupplierId From(int value)
{
return new SupplierId(value);
}
public override Validation Validate()
{
if (Value <= 0)
{
return Validation.Invalid("Supplier IDs cannot be zero or negative.");
}
return Validation.Ok;
}
public override object Value { get; }
}
``` cs
public class Age : ValueObject
{
public Age(int value)
{
Value = value;
}
public static Age From(int value)
{
return new Age(value) { Value = value };
}
public override Validation Validate()
{
if (Value <= 0)
{
return Validation.Invalid("Age cannot be zero or negative.");
}
return Validation.Ok;
}
public override object Value { get; }
}
``` cs
public class Score : ValueObject
{
public Score(int value)
{
Value = value;
}
public static Score From(int value)
{
return new Score(value) { Value = value };
}
public override Validation Validate()
{
if (Value <= 0)
{
return Validation.Invalid("Score cannot be zero or negative.");
}
return Validation.Ok;
}
public override object Value { get; }
}
Top-Level Directory Explanations
samples/ - This directory contains example projects demonstrating the usage of StringlyTyped library.
src/ - This directory contains the source code of the StringlyTyped library.
tests/ - This directory contains unit tests for the StringlyTyped library. It includes benchmark tests and small tests.