7

我对引用类型中的内容相等性有点困惑。在这两种情况下,我都没有压倒平等 - 那么为什么行为不同。

请参阅 2 个简单的代码示例:

示例 1:返回 True

class Program
{
    static void Main(string[] args)
    {
        object o1 = "ABC";
        object o2 = "ABC";

        Console.WriteLine("object1 and object2: {0}", o1.Equals(o2));
    }
}

示例 2:两个语句都返回 False

class Program
{
    static void Main(string[] args)
    {
        Person person1 = new Person("John");
        Person person2 = new Person("John");

        Console.WriteLine("person1 and person2: {0}", person1.Equals(person2));
        Console.WriteLine("person1 and person2: {0}", ((object)person1).Equals((object)person2));

        Console.ReadLine();
    }
}

public class Person
{
    private string personName;

    public Person(string name)
    {
        this.personName = name;
    }
}
4

7 回答 7

8

这里有两个作用:

  • 字符串实习意味着实际上即使您执行引用相等检查,您仍然会看到True. 您可以像这样修复它:

    object o1 = new StringBuilder("ABC").ToString();
    object o2 = new StringBuilder("ABC").ToString();
    
  • System.String 覆盖Equals比较字符串内容方法:

    此方法执行序数(区分大小写和不区分区域性)比较。

    你可以在这里看到不同之处:

    object o1 = new StringBuilder("ABC").ToString();
    object o2 = new StringBuilder("ABC").ToString();
    Console.WriteLine(o1.Equals(o2)); // Prints True due to overriding
    Console.WriteLine(ReferenceEquals(o1, o2)); // Prints False
    

你的类没有覆盖Equals,所以你得到了默认实现Object,即比较引用:

如果当前实例是引用类型,则 Equals(Object) 方法测试引用相等性,并且对 Equals(Object) 方法的调用等效于对 ReferenceEquals 方法的调用。

您可以通过覆盖来相当容易地解决这个问题Equals

// It's easier to implement equality correctly on sealed classes
public sealed class Person
{
    private readonly string personName;

    public Person(string name)
    {
        if (name == null)
        {
            throw new ArgumentNullException("name");
        }
        this.personName = name;
    }

    public override bool Equals(object other)
    {
        Person person = other as Person;
        return person != null && person.personName.Equals(personName);
    }

    // Must override GetHashCode at the same time...
    public override int GetHashCode()
    {
        // Just delegate to the name here - it's the only thing we're
        // using in the equality check
        return personName.GetHashCode();
    }
}

请注意,在Equals实现中我们可以使用:

return person != null && person.personName == personName;

...因为string 使运算符重载==。但那是另一回事:)

于 2013-08-14T16:12:09.467 回答
3

示例 1 返回true,因为Equals是比较值,因为对象被视为字符串;而示例 2 是比较对象的实例,并且由于它们各自指向不同的内存块,它们并不相等。

于 2013-08-14T16:10:01.913 回答
2

默认情况下,引用Equals默认ReferenceEquals为引用类型。你必须实例Person,所以它返回false

String覆盖Equals以实现值语义。因此,如果您比较两个string具有相同值的单独实例,则使用Equals它返回 true。

由于 string interning 都"ABC"指向同一个实例。因此,甚至ReferenceEqualstrue在您的第一个示例中返回。

编译时类型无关紧要,Equals因为它是一个虚拟方法。所以你的演员表object没有效果。投射到对象只会影响==!=因为它们是重载的,而不是被覆盖的。

于 2013-08-14T16:11:50.653 回答
1

字符串上的等号运算符被覆盖以逐字节比较字符串。在您的第二个示例中,您正在比较实例,即内存地址,这对于 Person() 的两个实例是不同的

于 2013-08-14T16:09:38.853 回答
0

在第一种情况下,String.Equals覆盖Object.Equals并检查实际的字符串值。没有使用引用相等。在第二种情况下,使用默认的引用相等。

但是,如果您使用object.ReferenceEquals,您会看到由于string interning导致的相同行为,这会导致前两个对象指向内存中的相同字符串(相同的引用),因为您使用的是字符串文字。

于 2013-08-14T16:09:15.707 回答
0

String已经覆盖了它的Equals方法,所以如果两个字符串以相同的顺序包含完全相同的字符,那么它们就是Equal. 您Person没有这样的覆盖,因此继承自使用引用等于作为其平等仲裁者的对象。

于 2013-08-14T16:12:44.330 回答
0

字符串引用相等,因为 .net 中的字符串实习(你会用谷歌搜索它)

于 2013-08-14T16:12:52.070 回答