title: Value type equality and strongly named assemblies
body: <p>We're developing plugins at our company. In order to test our code better, we have to mock parts of the host application and use that <em>fake</em> assembly as a dependency in our tests. Since the original assembly is strongly named we have to disable strong name validation via the Windows registry, otherwise the mocked assembly won't be accepted as a disguise for the original assembly.</p>
  <p>Within that assembly there is a type <code>Point</code>. I will refer to the original type definition by <code>Original.Point</code> and the mocked type by <code>Mocked.Point</code>. To be clear, <code>Mocked.Point</code> replaces <code>Original.Point</code>. Both types have 3 <code>float</code> fields for X, Y and Z. <code>Original.Point</code> has a few properties and methods that <code>Mocked.Point</code> doesn't have, but those shouldn't matter for equality.</p>
  <p>Here is the source of <code>Mocked.Point</code>:</p>
  <pre><code>public struct Point
  {
      public float X { get; }
      public float Y { get; }
      public float Z { get; }
  
      public Point3f(float x, float y, float z)
      {
          X = x;
          Y = y;
          Z = z;
      }
  }
  </code></pre>
  <p>In our tests, we call a function <code>Original.Point Host.GePoint()</code>. <code>Host</code> refers to a third assembly from the plugin SDK. That assembly is <strong>not</strong> mocked. We call the function like this:</p>
  <pre><code>Mocked.Point a = Host.GetPoint();
  </code></pre>
  <p>There is no conversion happening here. <code>Mocked.Point</code> is our replacement for <code>Original.Point</code>. As far as the runtime is concerned, both types are the same.</p>
  <p><strong>Case A</strong></p>
  <p>However if we do an equality comparison between two instances, say</p>
  <pre><code>Mocked.Point a = Host.GetPoint();
  Mocked.Point b = new(1, 0, 0); // b is equivalent to a, no rounding errors or anything
  a.Equals(b); // returns false
  </code></pre>
  <p>the equality check fails. I verified that <code>a.GetType() == b.GetType()</code>.</p>
  <p><strong>Case B</strong></p>
  <p>On the other hand, if we do</p>
  <pre><code>Mocked.Point a = new(1, 0, 0);
  Mocked.Point b = new(1, 0, 0);
  a.Equals(b); // returns true
  </code></pre>
  <p>the equality check succeeds.</p>
  <p>I stepped through the source of <code>System.ValueType</code> while debugging <code>a.Equals(b)</code>:</p>
  <pre><code>[SecuritySafeCritical]
  [__DynamicallyInvokable]
  public override bool Equals(object obj)
  {
    if (obj == null)
      return false;
    RuntimeType type = (RuntimeType) this.GetType();
    if ((RuntimeType) obj.GetType() != type)
      return false;
    object a = (object) this;
    if (ValueType.CanCompareBits((object) this))
      return ValueType.FastEqualsCheck(a, obj); // execution runs to here
    FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
    for (int index = 0; index &lt; fields.Length; ++index)
    {
      object obj1 = ((RtFieldInfo) fields[index]).UnsafeGetValue(a);
      object obj2 = ((RtFieldInfo) fields[index]).UnsafeGetValue(obj);
      if (obj1 == null)
      {
        if (obj2 != null)
          return false;
      }
      else if (!obj1.Equals(obj2))
        return false;
    }
    return true;
  }
  </code></pre>
  <p>and the execution path calls <code>ValueType.FastEqualsCheck</code> in the end in <strong>both</strong> cases, which is a compiler intrinsic<s>, so I cannot see what happens there (unless I look at the CLR source)</s>. This call does a <code>memcmp</code> on both values (<a href="https://github.com/dotnet/runtime/blob/07d17b67c168b4b47391b458c909658bcf56ff67/src/coreclr/vm/comutilnative.cpp#L1840" rel="nofollow noreferrer">source</a> ). This call returns <code>false</code> in case A and <code>true</code> in case B.</p>
  <p><strong>Question</strong></p>
  <p>Why does (or can) case A fail?</p>
  <hr>
  <p>I know this a very sketchy scenario but we can't work around this issue in any other way.</p>