C# 重载运算符==与等于()

C# 重载运算符==与等于(),c#,operator-overloading,equals,C#,Operator Overloading,Equals,我正在从事一个C#项目,到目前为止,我一直在使用不可变的对象和工厂来确保类型为Foo的对象始终可以与=进行相等的比较 Foo对象一旦创建就不能更改,工厂总是为给定的参数集返回相同的对象。这非常有效,在整个代码库中,我们假设==总是用于检查相等性 现在,我需要添加一些功能,引入一个边缘情况,这并不总是有效的。最简单的方法是为该类型重载操作符==,这样项目中的其他代码就不需要更改了。但这给我的印象是一种代码味道:重载操作符==和不等于似乎很奇怪,我习惯了=检查引用相等,而等于检查对象相等(或任何术语

我正在从事一个C#项目,到目前为止,我一直在使用不可变的对象和工厂来确保类型为
Foo
的对象始终可以与
=
进行相等的比较

Foo
对象一旦创建就不能更改,工厂总是为给定的参数集返回相同的对象。这非常有效,在整个代码库中,我们假设
==
总是用于检查相等性

现在,我需要添加一些功能,引入一个边缘情况,这并不总是有效的。最简单的方法是为该类型重载
操作符==
,这样项目中的其他代码就不需要更改了。但这给我的印象是一种代码味道:重载
操作符==
和不
等于
似乎很奇怪,我习惯了
=
检查引用相等,而
等于
检查对象相等(或任何术语)


这是一个合理的担忧,还是我应该继续并重载
操作符==

我相信标准是,对于大多数类型,.Equals检查对象相似性,操作符
=
检查引用相等性


我相信最佳实践是,对于不可变类型,操作符
=
应该检查相似性,以及
.Equals
。如果您想知道它们是否真的是同一个对象,请使用
.ReferenceEquals
。请参见C#
字符串
类以获取此示例。

对于不可变类型,我认为重载
=
以支持值相等没有任何问题。但是,我不认为我会重写
==
而不重写
Equals
来获得相同的语义。如果您确实重写了
==
,并且出于某种原因需要检查引用相等性,则可以使用
对象.ReferenceEquals(a,b)


看看这个,它确实有味道。重载
==
时,应确保
Equals()
GetHashCode()
也一致。看


这看起来还可以的唯一原因是您将您的类型描述为不可变的

重载
=
和覆盖之间有很大区别

当你有了这个表情

if (x == y) {
用于比较变量x和y的方法在编译时确定。这是运算符重载。声明x和y时使用的类型用于定义用于比较它们的方法。x和y中的实际类型(即子类或接口实现)是无关的。请考虑以下内容:

object x = "hello";
object y = 'h' + "ello"; // ensure it's a different reference

if (x == y) { // evaluates to FALSE
以及以下

string x = "hello";
string y = 'h' + "ello"; // ensure it's a different reference

if (x == y) { // evaluates to TRUE
string x = "hello";
string y = 'h' + "ello"; // ensure it's a different reference

if (x.Equals(y)) { // also evaluates to TRUE
这表明用于声明变量x和y的类型用于确定用于计算==的方法

通过比较,Equals是在运行时根据变量x中的实际类型确定的。Equals是对象上的一个虚拟方法,其他类型可以并确实重写它。因此,以下两个示例的计算结果均为真

object x = "hello";
object y = 'h' + "ello"; // ensure it's a different reference

if (x.Equals(y)) { // evaluates to TRUE
以及以下

string x = "hello";
string y = 'h' + "ello"; // ensure it's a different reference

if (x == y) { // evaluates to TRUE
string x = "hello";
string y = 'h' + "ello"; // ensure it's a different reference

if (x.Equals(y)) { // also evaluates to TRUE

根据微软自己的最佳实践,Equals方法和Equals(=)重载的结果应该是相同的


示例显示了如何根据(下文)实现此功能。注意,当重写等于时,还需要重写GetHashCode()。希望这对大家有帮助

public class Person
{
    public Guid Id { get; private set; }

    public Person(Guid id)
    {
        Id = id;
    }

    public Person()
    {
        Id = System.Guid.NewGuid();
    }

    public static bool operator ==(Person p1, Person p2)
    {
        bool rc;

        if (System.Object.ReferenceEquals(p1, p2))
        {
            rc = true;
        }
        else if (((object)p1 == null) || ((object)p2 == null))
        {
            rc = false;
        }
        else
        {
            rc = (p1.Id.CompareTo(p2.Id) == 0);
        }

        return rc;
    }

    public static bool operator !=(Person p1, Person p2)
    {
        return !(p1 == p2);
    }

    public override bool Equals(object obj)
    {
        bool rc = false;
        if (obj is Person)
        {
            Person p2 = obj as Person;
            rc = (this == p2);
        }
        return rc;
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }
}

否。引用类型的“标准”是两个Equals en==返回与referenceequals相同的值。不鼓励对非不可变类型重载。此外,请注意此处的null。x、 如果x为null,则等于(null)将抛出一个NRE,而null==x将起作用。“标准”是解决某些类型问题的常用方法,如果“标准”遇到阻碍,则它不是很好的标准。比尔,但它是一个很好的标准。您想要一种
a!=b
但是收集类认为它们是重复的吗?@Bill,我只是因为没有遵循标准而被咬了一口。这就是为什么我要问这里是什么。这应该足以表明标准的重要性。编码风格标准(用于提高组内的可读性)和实用编码标准(可避免很多麻烦)之间存在差异。这是非常重要的一点,我后来在测试时遇到了这一点。因为我需要
operator==
来依赖于运行时类型,所以我在基类中定义了一个名为
Equals()
的操作符重载。工作得很有魅力。(然后我重新定义了对象模型,把整个东西都撕掉了。但那是另一回事。)我完全不同意。使用
==
是一种常见的习惯用法。仅仅因为C#将所有实体都基于
对象
,并不意味着我们应该放弃这个习惯用法以防止出现本文中的情况。相反,当需要在层次结构顶部进行比较时,最好使用
.Equals()
。简言之,重载==设置要调用的方法(Object.Equals或Object.ReferenceEqual)。重载对象。等于检查实际值是否相等。我说得对吗?@Xaade,对
'
Equals
有不同的行为在我看来是非常危险的。它们应该被编程来做同样的事情,你可以提供
IEqualityComparer
的各种实现来提供替代的平等实现。哇,这是.NET设计中一个可怕的错误。仅仅因为我使用对象x而不是字符串x来定义变量,两个字符串就不相等的想法是一种有奖代码味道。谢谢你解释得这么好。我正在比较一个属性和一个methodinfo。这些在语义上是不可变的。顺便说一句,vb.net禁止对不提供显式重载的类型使用其
=
相等运算符;要检查引用是否相等,请选择一个