【发布时间】:2016-07-13 06:28:14
【问题描述】:
我有一些简单的结构,它覆盖了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()方法的值类型比较如下:
如果当前实例和 obj 的字段都不是引用类型,则 Equals 方法对内存中的两个对象进行逐字节比较。否则,它使用反射来比较obj和这个实例的对应字段。
我希望使用引用的方法而不是使用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() 方法替换比较。
编辑:
我真正的愿望是能够向这样的类添加代码协定:
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 相等。如果它改为使用原始的ValueType.Equals() 方法使用字节比较/反射,则将正确违反合同并且会发现错误。
如果使用对象,则合约将改为 Contract.Ensures(ReferenceEqual(_item, value),但这不适用于值类型(结构)。
关键是我不知道T 在Holder<T> 中的类型,所以我无法引入自己的自定义相等比较器,即使我想要。
【问题讨论】:
-
这是一个有趣的问题,但我能问一下目的是什么吗?你想完成什么更大的任务?
-
我有一个集合库(C5 by Sestoft),
Update(item)方法将使用其定义的相等比较器在集合中找到等于item的项目,并将其替换为 @ 987654343@。之后,我不能使用相等比较器来确保集合包含item,因为即使它只包含旧项目也是如此。如果我有一个对象,使用引用相等会很好,但对于没有意义的结构。但是,使用“原始”Equals行为会。 -
@lund.mikkel 您能否为您的收藏类型提供自定义
EqualityComparer<Pair<T>>? -
是的,但这正是我试图不使用/解决的问题。我的问题是我希望将代码合同添加到一个方法中,例如
Add(item)用于列表,以确保添加的项目实际上已添加到列表中。假设列表已经包含示例中的 p1a:如果我添加 p2,列表实现可以简单地复制 p1a,因为根据相等比较器它是相等的,并且合同将错误地确认该项目已添加,因为coll.Count(x => x.Equals(item))递增一个。我不能使用coll.Count(x => ReferenceEqual(x, item),因为 x 可能是一个结构
标签: c# reflection struct compare equals