title: Custom Equality check for C# 9 records
body: <p>From what I understand, records are actually classes that implement their own equality check in a way that your object is value-driven and not reference driven.</p>
  <p>In short, for the <code>record Foo</code> that is implemented like so: <code>var foo = new Foo { Value = &quot;foo&quot; }</code> and <code>var bar = new Foo { Value = &quot;foo&quot; }</code>, the <code>foo == bar</code> expression will result in <code>True</code>, even though they have a different reference (<code>ReferenceEquals(foo, bar) // False</code>).</p>
  <p>Now with records, even though that in <a href="https://devblogs.microsoft.com/dotnet/welcome-to-c-9-0/#value-based-equality" rel="noreferrer">the article</a> posted in .Net Blog, it says:</p>
  <blockquote>
  <p>If you don’t like the default field-by-field comparison behaviour of
  the generated Equals override, you can write your own instead.</p>
  </blockquote>
  <p>When I tried to place <code>public override bool Equals</code>, or <code>public override int GetHashCode</code>, or <code>public static bool operator ==</code>, and etc. I was getting <code>Member with the same signature is already declared</code> error, so I think that it is a restricted behaviour, which isn't the case with <code>struct</code> objects.</p>
  <p><a href="https://github.com/panosru/Playground/tree/7160550f3591dfb4b12c2722febed763d424980e/ValueObjects/ValueObjectsCS9Failing" rel="noreferrer">Failing example</a>:</p>
  <pre class="lang-cs prettyprint-override"><code>public sealed record SimpleVo
      : IEquatable&lt;SimpleVo&gt;
  {
      public bool Equals(SimpleVo other) =&gt;
          throw new System.NotImplementedException();
  
      public override bool Equals(object obj) =&gt;
          obj is SimpleVo other &amp;&amp; Equals(other);
  
      public override int GetHashCode() =&gt;
          throw new System.NotImplementedException();
  
      public static bool operator ==(SimpleVo left, SimpleVo right) =&gt;
          left.Equals(right);
  
      public static bool operator !=(SimpleVo left, SimpleVo right) =&gt;
          !left.Equals(right);
  }
  </code></pre>
  <p>Compiler result:</p>
  <pre><code>SimpleVo.cs(11,30): error CS0111: Type 'SimpleVo' already defines a member called 'Equals' with the same parameter types
  
  SimpleVo.cs(17,37): error CS0111: Type 'SimpleVo' already defines a member called 'op_Equality' with the same parameter types
  
  SimpleVo.cs(20,37): error CS0111: Type 'SimpleVo' already defines a member called 'op_Inequality' with the same parameter types
  </code></pre>
  <p>My main question here is what if we want to customise the way the equality checker works? I mean, I do understand that this beats the whole purpose of records, but on the other hand, equality checker is not the only feature that makes records cool to use.</p>
  <p>One use case where someone would like to override the equality of records is because you could have an <a href="https://github.com/panosru/Playground/blob/master/C%23/ValueObjects/ValueObjectsCS8/IgnoreMemberAttribute.cs" rel="noreferrer">attribute</a> that would exclude a property from equality check. Take for example <a href="https://github.com/panosru/Playground/blob/master/C%23/ValueObjects/ValueObjectsCS8/ValueObject.cs" rel="noreferrer">this</a> <code>ValueObject</code> implementation.</p>
  <p>Then if you extend this <code>ValueObject</code> abstract class like so:</p>
  <pre class="lang-cs prettyprint-override"><code>public sealed class FullNameVo : ValueObject
  {
      public FullNameVo(string name, string surname)
      {
          Name    = name;
          Surname = surname;
      }
  
      [IgnoreMember]
      public string Name { get; }
  
      public string Surname { get; }
  
      [IgnoreMember]
      public string FullName =&gt; $&quot;{Name} {Surname}&quot;;
  }
  </code></pre>
  <p>then you would get the following <a href="https://dotnetfiddle.net/Widget/wP9Fpm" rel="noreferrer">results</a>:</p>
  <pre class="lang-cs prettyprint-override"><code>var user1 = new FullNameVo(&quot;John&quot;, &quot;Doe&quot;);
  var user2 = new FullNameVo(&quot;John&quot;, &quot;Doe&quot;);
  var user3 = new FullNameVo(&quot;Jane&quot;, &quot;Doe&quot;);
  
  Console.WriteLine(user1 == user2); // True
  Console.WriteLine(ReferenceEquals(user1, user2)); // False
  Console.WriteLine(user1 == user3); // True
  Console.WriteLine(user1.Equals(user3)); // True
  </code></pre>
  <p>So far, in order to achieve <em>somehow</em> the above use case, I have implemented <a href="https://github.com/panosru/Playground/blob/master/C%23/ValueObjects/ValueObjectsCS9/ValueObject.cs" rel="noreferrer">an abstract record object</a> and utilise it like so:</p>
  <pre class="lang-cs prettyprint-override"><code>public sealed record FullNameVo : ValueObject
  {
      [IgnoreMember]
      public string Name;
  
      public string Surname;
  
      [IgnoreMember]
      public string FullName =&gt; $&quot;{Name} {Surname}&quot;;
  }
  </code></pre>
  <p>and the results look like this:</p>
  <pre class="lang-cs prettyprint-override"><code>var user1 = new FullNameVo
  {
      Name    = &quot;John&quot;,
      Surname = &quot;Doe&quot;
  };
  
  var user2 = new FullNameVo
  {
      Name    = &quot;John&quot;,
      Surname = &quot;Doe&quot;
  };
  
  var user3 = user1 with { Name = &quot;Jane&quot; };
  
  Console.WriteLine(user1 == user2); // True
  Console.WriteLine(ReferenceEquals(user1, user2)); // False
  Console.WriteLine(user1 == user3); // False
  Console.WriteLine(user1.Equals(user3)); // False
  Console.WriteLine(ValueObject.EqualityComparer.Equals(user1, user3)); // True
  </code></pre>
  <p>To conclude, I'm a bit puzzled, is restricting the override of equality methods of record objects an expected behaviour or is it because it is still in preview stage? If it is by design, would you implement the above behaviour in a different <em>(better)</em> way or you would just continue using classes?</p>
  <p><code>dotnet --version</code> output: <code>5.0.100-rc.1.20452.10</code></p>