Equality for a DateOfBirth Value Object Please see the code below:

public sealed class DateOfBirth : IEquatable&lt;DateOfBirth&gt;, IComparable&lt;DateOfBirth&gt;
    {
        private readonly DateTime _value;

        public DateTime Value
        {
            get { return _value; }
        }
        public DateOfBirth(DateTime dateOfBirth)
        {
            if(dateOfBirth == DateTime.MinValue)
                throw new ArgumentException("Invalid value.", "DateOfBirth");
            this._value= dateOfBirth;
        }

        private static int Comparison(DateOfBirth dateOfBirth1, DateOfBirth dateOfBirth2)
        {
            if (ReferenceEquals(dateOfBirth1, dateOfBirth2))
                return 0;
            else if (ReferenceEquals(dateOfBirth1, null))
                return -1;
            else if (ReferenceEquals(dateOfBirth2, null))
                return 1;
            if (dateOfBirth1._value &lt; dateOfBirth2._value)
                return -1;
            else if (dateOfBirth1._value == dateOfBirth2._value)
                return 0;
            else if (dateOfBirth1._value &gt; dateOfBirth2._value)
                return 1;
            return 0;
        }

        public int CompareTo(DateOfBirth other)
        {
            if (other != null)
                return this._value.CompareTo(other._value);
            else
                throw new ArgumentNullException("DateOfBirth");
        }


        public static bool operator ==(DateOfBirth dateOfBirth1, DateOfBirth dateOfBirth2)
        {
            return Comparison(dateOfBirth1, dateOfBirth2) == 0;
        }

        public static bool operator !=(DateOfBirth dateOfBirth1, DateOfBirth dateOfBirth2)
        {
            return !(dateOfBirth1 == dateOfBirth2);
        }

        public static bool operator &lt;(DateOfBirth dateOfBirth1, DateOfBirth dateOfBirth2)
        {
            return Comparison(dateOfBirth1, dateOfBirth2) &lt; 0;
        }

        public static bool operator &gt;(DateOfBirth dateOfBirth1, DateOfBirth dateOfBirth2)
        {
            return Comparison(dateOfBirth1, dateOfBirth2) &gt; 0;
        }

        public static bool operator &lt;=(DateOfBirth dateOfBirth1, DateOfBirth dateOfBirth2)
        {
            return Comparison(dateOfBirth1, dateOfBirth2) &lt;= 0;
        }

        public static bool operator &gt;=(DateOfBirth dateOfBirth1, DateOfBirth dateOfBirth2)
        {
            return Comparison(dateOfBirth1, dateOfBirth2) &gt;= 0;
        }

        public bool Equals(DateOfBirth other)
        {
            if (ReferenceEquals(other, null))
                return false;
            return _value == other._value;
        }

        public override bool Equals(object obj)
        {
            return Equals(obj as DateOfBirth);
        }

        public override int GetHashCode()
        {
            return _value.GetHashCode();
        }
    }


I realise it could be argued as over engineering a simple date of birth field.  I am more interested whether or not the nine comparisons (https://ericlippert.com/2013/10/07/math-from-scratch-part-six-comparisons/) are implemented correctly.  This article helped me: https://blogs.msdn.microsoft.com/abhinaba/2005/10/11/c-comparison-operator-overloading-and-spaceship-operator/ - the only concern I have about the code in this article is that the Comparison method does not do this (which my code does):

if (ReferenceEquals(dateOfBirth1, dateOfBirth2))
                return 0;
            else if (ReferenceEquals(dateOfBirth1, null))


Q1) Is the code above fit for purpose (are there any bugs)?

Q2) Is a DateTime data type (DateOfBirth._value) suitable for a Date of Birth?
                    return -1;

Q3) Is the validation in the constructor fit for purpose?
'fit for purpose' depends on what exactly that purpose is. Do you want to register the time of birth (as the Value property suggests), or only the date (as the class name seems to imply)? Does it need to handle birth dates before 0 A.D.? Does it need to reject non-existing dates (depending on actual historical data)?
If this class is meant to store a date only (not a time component), then it makes more sense to give it Year, Month and Day properties, rather than a Value property that exposes a time component.
DateTime.MinValue is the default value of DateTime, but why does that make it an invalid birth-date?
It may be useful to provide (explicit or implicit) conversions operators from and to DateTime, and perhaps parsing methods, depending on where and how this class is meant to be used. However...
...the purpose of this class seems very narrow. Judging from your other posts, you are thinking about creating many such 'value' types? That's a lot of work (at least in C#), but what problem does it solve? Where's the added value? Are you worried that someone may accidentally assign an expiry-date to a birth-date field? Remember, all that extra code needs to be maintained, and there's a cost associated with that.


My impression is that yes, you are over-engineering this. I suspect that you'll find more value in writing automated tests (which, when done properly, should have caught that &gt;= operator bug, and should catch expiry-date/birth-date mixups just as well) than in adding this sort of complexity. C# requires too much boiler-plate to make this approach viable, so unless lives are at stake I wouldn't bother.



EDIT: A few more notes:


In C# 7, ReferenceEquals(a, null) can be written more succinctly as a is null.
The null check in CompareTo uses the overloaded != operator, but you might as well use a reference check directly.
Why are the Equals methods not using Comparison? The same goes for the != operator: why doesn't it do Comparison(dateOfBirth1, dateOfBirth2) != 0?