C#中的通用值相等性(身份)

6

如何以最简洁和最优的方式比较两个相同泛型类型的实例,使得引用类型比较其身份(同一对象,因此不调用Equals),而值类型比较其相等性

目前我这样做:

static bool IdentityEquals<T>(T x, T y)
{
    return typeof(T).IsValueType
        ? EqualityComparer<T>.Default.Equals(x, y)
        : ReferenceEquals(x, y);
}

C# 可以通过泛型细化进行重载吗? - user2246674
3个回答

5

对于值类型,您应该可以直接使用object.Equals

 return typeof(T).IsValueType
    ? object.Equals(x, y)
    : ReferenceEquals(x, y);

1
由于装箱,这会变得更慢。 - Marcin Wisnicki
5
好的,我将尽力为您提供准确的翻译。下面是需要翻译的内容:@MarcinWisnicki you did ask for "best (most concise)" - Blorgbeard
1
抱歉,我已经更新了需求,也包括“最优”的要求;-P 我真的希望有一些内置方法可以进行身份比较,就像ReferenceEquals()一样,但不会在值类型上失败。 - Marcin Wisnicki
@MarcinWisnicki,两种方法都涉及装箱。 - Andrew Savinykh
@zespri 实际上内置的值类型(int,string等)都实现了IEquatable<T>,这通常可以避免在使用EqualityComparer<T>时进行装箱。 - D Stanley
@MarcinWisnicki 简洁与高性能可能会有竞争目标。你可能需要选择其中哪个更重要。 - D Stanley

0

不幸的是,.NET只定义了一个标准虚拟Equals方法和一个GetHashCode方法,即使有两个逻辑问题可以询问(如果假设XY是相同类型的变量,则这些问题最简单):

  1. 是否可以将`X`替换为对`Y`的引用,反之亦然,而不影响它们的任何成员的现在或未来行为?
  2. 如果同时使所有指向`X`目标的引用都指向`Y`,并且所有指向`Y`目标的引用都指向`X`,那么`X`和`Y`的所有成员是否仍将继续以相同的方式运行?

假设XY持有对类型为int [1]的两个对象的引用,这些对象当前包含值42。一些外部代码持有与Y相同实例的引用,并将要对其进行增量。如果将X更改为指向与Y相同的实例,则会更改X [0]的未来值。

另一方面,假设两个对象都有一个私有字段,每个字段都持有它自己的 int[1] 的唯一引用,这是整个宇宙中唯一存在的引用。这两个对象都持有并且将永远持有值 42,并且没有调用过 RuntimeHelpers.GetHashCode() 方法来获取该实例的哈希码。尽管 int[1] 是可变类型,但是这两个实例应被视为等价的,因为交换第一个实例的所有引用(即唯一的引用)与第二个实例的所有引用不会影响程序行为。然而,如果一个数组持有 23,另一个数组持有 27,则交换引用将交换哪个对象持有对 23 的引用,哪个对象持有对 27 的引用。
请注意,如果添加语句“不同类型的对象不能等效,因为它们的GetType()方法必然会有不同的行为”,那么上述两个问题都可以对任何类类型的对象进行有意义和明确的回答,并且第二个问题可以对任何值类型进行有意义的回答。这些答案将构成等价性和值相等性的有意义定义。.NET Equals重写的正常行为倾向于让引用类型回答第一个问题(代码更可能对其感兴趣),并让值类型回答第二个问题(因为第一个问题对它们来说是无意义的)。由于听起来默认行为是您想要的,也许您可以描述一下是否存在某种情况,其中它不是默认行为?
[注意:一些.NET类型(如Decimal)以松散的方式定义Equals;上述两个问题将是通用虚拟方法所需回答的好问题]。

0

要么在您感兴趣的类型上实现IEquatable<T>,并重写object.Equals()object.GetHashCode(),要么为您的类型使用者提供IEqualityComparer<T>的实例。

DOT NET不会实现您定义的相等性,因为那样它就不会是我的,也不会是街上的Fred的。相反,它为您提供了构建所需工具的机会,而不会妨碍我构建所需的内容。


是的,当涉及到对象的相等性和身份时,.NET Framework的故事非常令人困惑。因此,弗雷德的定义很可能会有所不同。请参阅J Richter的《CLR via C#》第5章,了解微软如何以更少的困扰来完成这个任务。 - Andrew Savinykh
1
实际上,身份的概念已经被很好地定义,并且绝对是实现流行设计模式(如观察者 - 您必须区分对象身份以跟踪正确的对象)的基础。 - Marcin Wisnicki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接