How do you choose between implementing a value object (the canonical example being an address) as an immutable object or a struct?
Are there performance, semantic or
I like to use a thought experiment:
Does this object make sense when only an empty constructor is called?
Edit at Richard E's request
A good use of struct is to wrap primitives and scope them to valid ranges.
For example, probability has a valid range of 0-1. Using a decimal to represent this everywhere is prone to error and requires validation at every point of usage.
Instead, you can wrap a primitive with validation and other useful operations. This passes the thought experiment because most primitives have a natural 0 state.
Here is an example usage of struct to represent probability:
public struct Probability : IEquatable, IComparable
{
public static bool operator ==(Probability x, Probability y)
{
return x.Equals(y);
}
public static bool operator !=(Probability x, Probability y)
{
return !(x == y);
}
public static bool operator >(Probability x, Probability y)
{
return x.CompareTo(y) > 0;
}
public static bool operator <(Probability x, Probability y)
{
return x.CompareTo(y) < 0;
}
public static Probability operator +(Probability x, Probability y)
{
return new Probability(x._value + y._value);
}
public static Probability operator -(Probability x, Probability y)
{
return new Probability(x._value - y._value);
}
private decimal _value;
public Probability(decimal value) : this()
{
if(value < 0 || value > 1)
{
throw new ArgumentOutOfRangeException("value");
}
_value = value;
}
public override bool Equals(object obj)
{
return obj is Probability && Equals((Probability) obj);
}
public override int GetHashCode()
{
return _value.GetHashCode();
}
public override string ToString()
{
return (_value * 100).ToString() + "%";
}
public bool Equals(Probability other)
{
return other._value.Equals(_value);
}
public int CompareTo(Probability other)
{
return _value.CompareTo(other._value);
}
public decimal ToDouble()
{
return _value;
}
public decimal WeightOutcome(double outcome)
{
return _value * outcome;
}
}