使用标准方法(字节 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()
方法来比较 Pair
s,以便通过以下测试:
[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>));
}
使用上述方法检索 ValueTypes
的 Equal()
方法,示例中的测试将如下所示:
[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));
}
我有一些简单的结构,它覆盖了 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()
方法来比较 Pair
s,以便通过以下测试:
[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>));
}
使用上述方法检索 ValueTypes
的 Equal()
方法,示例中的测试将如下所示:
[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));
}