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.