Date of Birth Value Object 2 I realize it could be argued that it is an overkill to create a ValueObject for such a simple type. I am just trying to make sure that I understand the principles of overriding/overloading these methods correctly. I would be grateful for some critical feedback. Specifically:


Are all the methods implemented correctly?
Have I compared the date of births correctly i.e. if (candidate.AddYears(21).Date &gt; DateTime.Now.Date) assuming that I am using the western idea of age.


Please note that with regards to naming conventions; I have used what is advised here

public sealed class DateOfBirth
    {
        private readonly DateTime _value;
        public DateOfBirth(DateTime value)
        {
            if (value.TimeOfDay.TotalSeconds &gt; 0)
                throw new ArgumentException("Date of birth cannot contain a time.", "DateOfBirth");
            if (IsValid(value) == true)
            {
                _value = value;
            }
        }

        public DateOfBirth(int year, int month, int day)
        {
            var value = new DateTime(year, month, day);
            if (IsValid(value)==true)
            {
                _value = value;
            }     
        }

        public static bool IsValid(DateTime candidate)
        {
            if (candidate.AddYears(21).Date &gt; DateTime.Now.Date) 
                throw new ArgumentException("Age must be greater than 21.", "DateOfBirth");
            return true;
        }

        public DateTime Value
        {
            get
            {
                return _value;
            }
        }

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

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

        public static implicit operator DateTime(DateOfBirth dateOfBirth)
        {
            return dateOfBirth._value;
        }

        public static bool operator ==(DateOfBirth dateOfBirth1, DateOfBirth dateOfBirth2)
        {
            if (!ReferenceEquals(dateOfBirth1, null) &amp;&amp;
                ReferenceEquals(dateOfBirth2, null))
            {
                return false;
            }
            if (ReferenceEquals(dateOfBirth1, null) &amp;&amp;
                 !ReferenceEquals(dateOfBirth2, null))
            {
                return false;
            }
            if (ReferenceEquals(dateOfBirth1, null) &amp;&amp;
                 ReferenceEquals(dateOfBirth2, null))
            {
                return true;
            }
            return dateOfBirth1.Equals(dateOfBirth2);
        }

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

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


Note: this is a follow-up question to this one.
Design:


I still think that a DateOfBirth class is too narrow. It should be the name of a property, not a separate class. If you must create a class, create a reusable Date class instead. Enforcing this sort of restrictions at the type level isn't worth the extra work and maintenance cost (in C#). That's my opinion, of course, but judging by your many questions it really is a lot of work, isn't it? ;)
I expect a DateOfBirth.IsValid method to check whether the given input is a valid birth-date. That is, I expect it to reject nonsense input like '-5 45 2018'. However, your implementation enforces a business rule instead. There are several problems with that:


It's confusing: it's basically telling me that '7 februari 2018' is not a valid birthdate.
It's inflexible: what if some logic applies to people above 21, but other logic only to people below 18?

Again, why expose Value directly? As I mentioned before, a date does not have a time component, but by exposing a DateTime property you make it look like it does. Of course, you may want to convert between dates and date-times, but that's what conversion operators are for. A Year, Month and Day property are far more in line with what a date actually is. Look at DateTime, for example: it uses a timestamp as internal storage, but it provides meaningful properties like Year, Month, Day, Hour, Minute, Second, and so on, which results in a much more natural and practically useful interface.
Where did the comparison operators go? Are they superseded by the IsValid method? Or are you planning to use Value for that? If so, why create a separate class in the first place?


Implementation:


No need for == true: IsValid already returns a boolean.
The constructors do not throw if validation fails: they fall back to a 'default' value instead. That's very surprising behavior, in a bad way. It's also inconsistent, because some input does produce an exception. (EDIT: Excuse me, they do throw, because IsValid throws - so that underlines the point below! ThrowIfInvalid would be a more appropriate method name.)
Why does IsValid throw an exception? Its signature suggests that it will only return true/false. Such unexpected behavior makes code more difficult to work with.
The value property can be written more succinctly as public DateTime Value =&gt; _value;, but you could also use a get-only property instead.
You may want to mention which version of C# you're using. I mentioned the a is null improvement from C#7, but I don't see that applied here
so I guess you're still stuck with an older version?


Finally, the == operator can be simplified:

if (ReferenceEquals(a, null) != ReferenceEquals(b, null))
{
    return false;       // One is null, the other is not
}
else if (ReferenceEquals(a, null))
{
    return true;        // Both are null
}
else
{
    return a.Equals(b); // Both are not null
}