使用标准方法(字节 Comparison/Reflection)比较两个结构,即使存在 Equals 方法

Compare Two Structs Using Standard Method (Byte Comparison/Reflection) Even If Equals Method Exists

我有一些简单的结构,它覆盖了 Equals() 方法:

public struct Pair<T> {
    public Pair(T x, T y) {
        X = x; Y = y;
    }

    public T X { get; }
    public T Y { get; }

    public override bool Equals(object obj) {
        var otherPair = (Pair<T>) obj;
        return X.Equals(otherPair.X);
    }
}

根据 MSDN,没有 Equals() 方法的值类型比较如下:

If none of the fields of the current instance and obj are reference types, the Equals method performs a byte-by-byte comparison of the two objects in memory. Otherwise, it uses reflection to compare the corresponding fields of obj and this instance.

我希望使用引用的方法而不是使用 Pair 自己的 Equals() 方法来比较 Pairs,以便通过以下测试:

[Test]
public void PairsEqual()
{
    var p1a = new Pair<int>(10, 1);
    var p1b = new Pair<int>(10, 1);
    var p2 = new Pair<int>(10, 2);

    Assert.That(p1a, Is.Not.EqualTo(p2));
    Assert.That(p1a, Is.EqualTo(p1a));
    Assert.That(p1a, Is.EqualTo(p1b));
}

这最终应该像结构的 ReferenceEqual 一样工作。这可能吗?理想情况下,我想用原来的 ValueType.Equals() 方法替换比较。

编辑:

我真正的愿望是能够像这样向 class 添加代码合约:

public class Holder<T> {
    private T _item;

    public Holder(T item) {
        _item = item;
    }

    public T Item {
        get { return _item; }
        set {
            Contract.Ensures(_item.Equals(value));
            _item = value; // <-- imagine this like contained a bug
        }
    }
}

假设我像这样使用 holder 对象:

var holder = new Holder<Pair<int>>(p1a);
holder.Item = p2;

如果 set 不是 _item = value; 而是 _item = _item;,合同不会抱怨,因为示例将使用 Pair<T>Equals() 方法,表示 p1a 和 p2 相等。如果它改为使用使用字节 comparison/reflection 的原始 ValueType.Equals() 方法,则会正确违反合同并且会发现错误。

使用对象,合同会变成类似 Contract.Ensures(ReferenceEqual(_item, value) 的东西,但这不适用于值类型(结构)。

关键是我不知道Holder<T>T的类型,所以我不能引入我自己的自定义相等比较器,即使我想。

这可以使用反射来完成。以下解决方案基于博客 post Strong Typed, High Performance Reflection with C# Delegate 中的代码,但代码已缩短以专门用于 ValueType.Equals():

public static Func<ValueType, ValueType, bool> GetValueTypeEquals()
{
    var type = typeof(ValueType);
    var dynamicMethod = new DynamicMethod("ValueTypeEquals", typeof(bool), new[] { type, typeof(object) }, type);
    var il = dynamicMethod.GetILGenerator();
    il.Emit(OpCodes.Ldarg, 0);
    il.Emit(OpCodes.Ldarg, 1);
    il.EmitCall(OpCodes.Call, type.GetMethod(nameof(Equals), Public | Instance, null, new[] { type }, null), null);
    il.Emit(OpCodes.Ret);

    return (Func<ValueType, ValueType, bool>) dynamicMethod.CreateDelegate(typeof(Func<ValueType, ValueType, bool>));
}

使用上述方法检索 ValueTypesEqual() 方法,示例中的测试将如下所示:

[Test]
public void PairsEqual()
{
    var p1a = new Pair<int>(10, 1);
    var p1b = new Pair<int>(10, 1);
    var p2 = new Pair<int>(10, 2);

    var equals = GetValueTypeEquals();

    Assert.That(!equals(p1a,  p2));
    Assert.That(equals(p1a, p1a));
    Assert.That(equals(p1a, p1b));
}