# C# Equality Operator Overloading

Here’s a good example for how to implement operator for equality (==, !=, .Equals()).

This is taken from the Version3 type in OGA.SharedKernel.Lib.

NOTE: These overrides include method signatures for older and newer NET Framework versions.

## Equal / Not Equal (==, !=, Equals())<button aria-hidden="false" aria-label="Copy link to heading" class="cc-1r0b9w7" data-testid="anchor-button" tabindex="-1" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

This first block is for equality and inequality overloading.

```c#
        #region Operator Overloads

        /// <summary>
        /// Implements the IEquatable interface, same as the native Version class.
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        // Force inline as the true/false ternary takes it above ALWAYS_INLINE size even though the asm ends up smaller
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#if (NET452 || NET47 || NET48)
        public static bool operator ==(cVersion3 v1, cVersion3 v2)
#else
        public static bool operator ==(cVersion3? v1, cVersion3? v2)
#endif
        {
            // Test "right" first to allow branch elimination when inlined for null checks (== null)
            // so it can become a simple test
            if (v2 is null)
            {
                // return true/false not the test result https://github.com/dotnet/runtime/issues/4207
                return (v1 is null) ? true : false;
            }

            // Quick reference equality test prior to calling the virtual Equality
            return ReferenceEquals(v2, v1) ? true : v2.Equals(v1);
        }

#if (NET452 || NET47 || NET48)
        /// <summary>
        /// Implements the IEquatable interface, same as the native Version class.
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        public static bool operator !=(cVersion3 v1, cVersion3 v2) => !(v1 == v2);
#else
        /// <summary>
        /// Implements the IEquatable interface, same as the native Version class.
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        public static bool operator !=(cVersion3? v1, cVersion3? v2) => !(v1 == v2);
#endif

        #endregion
```

<div class="highlighter-context page view" data-inline-comments-target="true" data-testid="page-content-only" id="bkmrk-"><div class="_19pkidpf _2hwx1wug _otyridpf _18u01wug _1bsb1osq"><div><div class="wiki-content css-2xmmiu e5xcnr80" data-test-appearance="full-page" data-testid="pageContentRendererTestId"><div class="renderer-overrides"><div class="ak-renderer-wrapper is-full-page cc-1jke4yk"><div class="cc-1ll6l5u"><div class="ak-renderer-document"><div class="fabric-editor-breakout-mark fabric-editor-block-mark cc-p8f2xz" data-has-width="false" data-mode="wide"><div class="code-block  cc-wroouh">  
</div></div></div></div></div></div></div></div></div></div>The above equality logic requires an override of Equals().  
Here’s what that looks like:

NOTE: The virtual Equals() method that we override accepts a type of Object.  
So, we include an override for that, and a type-specific public overload of the same method name that does the work for both.

```c#
#if (NET452 || NET47 || NET48)
        /// <summary>
        /// Implementation of the IEquatable interface.
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
#else
        /// <summary>
        /// Implementation of the IEquatable interface.
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals([NotNullWhen(true)] object? obj)
#endif
        {
            return Equals(obj as cVersion3);
        }

#if (NET452 || NET47 || NET48)
        /// <summary>
        /// Implementation of the IEquatable interface.
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public bool Equals(cVersion3 obj)
#else
        /// <summary>
        /// Implementation of the IEquatable interface.
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public bool Equals([NotNullWhen(true)] cVersion3? obj)
#endif
        {
            return object.ReferenceEquals(obj, this) ||
                (!(obj is null) &&
                _Major == obj._Major &&
                _Minor == obj._Minor &&
                _Patch == obj._Patch);
        }

```

<div class="highlighter-context page view" data-inline-comments-target="true" data-testid="page-content-only" id="bkmrk-when-overriding-the-"><div class="_19pkidpf _2hwx1wug _otyridpf _18u01wug _1bsb1osq"><div class="wiki-content css-2xmmiu e5xcnr80" data-test-appearance="full-page" data-testid="pageContentRendererTestId"><div class="renderer-overrides"><div class="ak-renderer-wrapper is-full-page cc-1jke4yk"><div class="cc-1ll6l5u"><div class="ak-renderer-document">  
</div><div class="ak-renderer-document">When overriding the == operator, like above, the compiler will present warnings if you have not overridden the GetHashCode() method. Overriding it, is straightforward, but has a different implementation in modern NET (NETCore) than classic NET Framework.</div><div class="ak-renderer-document">This is because modern NET added the HashCode.Combine() method, that makes things much easier.</div><div class="ak-renderer-document">  
</div><div class="ak-renderer-document">So, here's an example of how to override the GetHashCode method, that includes both modern and classic methods:</div><div class="ak-renderer-document">  
</div></div></div></div></div></div></div>```c#
        /// <summary>
        /// Public override of GetHashCode to satisfy compiler warning for overriding Equality.
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
#if (NET452 || NET47 || NET48)
            int hash = 11;
            hash = hash * 18 + this._Major.GetHashCode();
            hash = hash * 18 + this._Minor.GetHashCode();
            hash = hash * 18 + this._Patch.GetHashCode();
            return hash;
#else
            return HashCode.Combine(this._Major, this._Minor, this._Patch);
#endif
        }
```

## Comparison Overloading (&gt;, &lt;, &gt;=, &lt;=)<button aria-hidden="false" aria-label="Copy link to heading" class="cc-1r0b9w7" data-testid="anchor-button" tabindex="-1" type="button"><svg height="24" role="presentation" viewbox="0 0 24 24" width="24"></svg></button>

And, this block is for greater and less than overloading:

```c#
        #region Operator Overloads

#if (NET452 || NET47 || NET48)
        /// <summary>
        /// Implements the IEquatable interface, same as the native Version class.
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        public static bool operator <(cVersion3 v1, cVersion3 v2)
#else
        /// <summary>
        /// Implements the IEquatable interface, same as the native Version class.
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        public static bool operator <(cVersion3? v1, cVersion3? v2)
#endif
        {
            if (v1 is null)
            {
                return !(v2 is null);
            }

            return v1.CompareTo(v2) < 0;
        }

#if (NET452 || NET47 || NET48)
        /// <summary>
        /// Implements the IEquatable interface, same as the native Version class.
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        public static bool operator <=(cVersion3 v1, cVersion3 v2)
#else
        /// <summary>
        /// Implements the IEquatable interface, same as the native Version class.
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        public static bool operator <=(cVersion3? v1, cVersion3? v2)
#endif
        {
            if (v1 is null)
            {
                return true;
            }

            return v1.CompareTo(v2) <= 0;
        }

#if (NET452 || NET47 || NET48)
        /// <summary>
        /// Implements the IEquatable interface, same as the native Version class.
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        public static bool operator >(cVersion3 v1, cVersion3 v2) => v2 < v1;
#else
        /// <summary>
        /// Implements the IEquatable interface, same as the native Version class.
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        public static bool operator >(cVersion3? v1, cVersion3? v2) => v2 < v1;
#endif

#if (NET452 || NET47 || NET48)
        /// <summary>
        /// Implements the IEquatable interface, same as the native Version class.
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        public static bool operator >=(cVersion3 v1, cVersion3 v2) => v2 <= v1;
#else
        /// <summary>
        /// Implements the IEquatable interface, same as the native Version class.
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        public static bool operator >=(cVersion3? v1, cVersion3? v2) => v2 <= v1;
#endif

        #endregion
```

<div class="highlighter-context page view" data-inline-comments-target="true" data-testid="page-content-only" id="bkmrk--2"><div class="_19pkidpf _2hwx1wug _otyridpf _18u01wug _1bsb1osq"><div><div class="wiki-content css-2xmmiu e5xcnr80" data-test-appearance="full-page" data-testid="pageContentRendererTestId"><div class="renderer-overrides"><div class="ak-renderer-wrapper is-full-page cc-1jke4yk"><div class="cc-1ll6l5u"><div class="ak-renderer-document"><div class="fabric-editor-breakout-mark fabric-editor-block-mark cc-p8f2xz" data-has-width="false" data-mode="wide">  
</div></div></div></div></div></div></div></div></div>NOTE: The above comparison overloads requires a type-specific CompareTo() implementation override.

So, here’s one that you can work from:

NOTE: The virtual CompareTo() method that we override accepts a type of Object.  
So, we include an override for that, and a type-specific public overload of the same method name that does the work for both.

```c#
#if (NET452 || NET47 || NET48)
        /// <summary>
        /// Implementation of the IComparable interface.
        /// </summary>
        /// <param name="version"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public int CompareTo(object version)
#else
        /// <summary>
        /// Implementation of the IComparable interface.
        /// </summary>
        /// <param name="version"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public int CompareTo(object? version)
#endif
        {
            if (version == null)
            {
                return 1;
            }

            if (version is cVersion3 v)
            {
                return CompareTo(v);
            }

            throw new ArgumentException("Invalid Version Instance.");
        }

#if (NET452 || NET47 || NET48)
        /// <summary>
        /// Implementation of the IComparable interface.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public int CompareTo(cVersion3 value)
#else
        /// <summary>
        /// Implementation of the IComparable interface.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public int CompareTo(cVersion3? value)
#endif
        {
            if (value == null)
            {
                return 1;
            }

            return
                object.ReferenceEquals(value, this) ? 0 :
                value is null ? 1 :
                _Major != value._Major ? (_Major > value._Major ? 1 : -1) :
                _Minor != value._Minor ? (_Minor > value._Minor ? 1 : -1) :
                _Patch != value._Patch ? (_Patch > value._Patch ? 1 : -1) :
                0;
        }
```