对象的比较取决于它的价值

时间:2013-12-13 12:03:53

标签: c# .net equality

假设我们有代码:

void Main()
{
    object a = new object();
    a = 5;
    object b = new object();
    b = 5;
    Console.WriteLine (a == b);       // False
    Console.WriteLine(a.Equals(b));   // True

    Console.WriteLine();

    object s1 = new object();
    s1 = "5";
    object s2 = new object();
    s2 = "5";
    Console.WriteLine(s1 == s2);       // True
    Console.WriteLine(s1.Equals(s2));  // True
}

问题是:为什么这种行为是不同的,并且依赖于存储在对象中的类型。

修改

澄清问题,以及为什么它不是一个复杂的问题。主要问题是==行为会在这种情况下产生不同的反应:请参阅@ LesseV.Karlsen评论。

2 个答案:

答案 0 :(得分:2)

之所以这样:

object o1 = 5;
object o2 = 5;
bool b = ReferenceEquals(o1, o2);

不会产生与此相同的结果:

object o1 = "5";
object o2 = "5";
bool b = ReferenceEquals(o1, o2);

是因为在第一种情况下,C#的伪代码版本看起来像这样:

object o1 = box(5);
object o2 = box(5);
bool b = ReferenceEquals(o1, o2);

两个框操作会将值5分成两个单独的对象。可以想象,在CPU速度无限但内存不丰富的世界中,人们可能想要扫描内存以查看我们是否有一个盒装int 5的现有对象,因此只需指向实例,因此o1o2将是同一个实例。

但是,这不存在,所以在第一种情况下,o1o2将对内存中的两个单独对象进行两次引用,两者都包含一个盒装int 5。 / p>

然而,在第二段代码中,有些"魔法"使它的行为与第一件不同,因为JITter将会实现"实习生"字符串。基本上,代码中使用的两个字符串"5"在内存中只是1个字符串。这意味着,在这种情况下,o1o2实际上将包含相同的引用。

基本上是:

object o1 = string.Intern("5");
object o2 = string.Intern("5");
bool b = ReferenceEquals(o1, o2);

请注意,这与上面的无限CPU不同(假设)情况。基本上,JITter将构建它在JITting期间发现的字符串的内部数据结构,而不是为具有相同内容的两个单独(常量)字符串创建对象,而是在内部数据结构中查找它,并且&# 39;为什么代码中的两个字符串文字将在运行时引用内存中的同一个单个对象。

我对实习生方法的天真假设实施可能是这样的:

string original;
if (internDictionary.TryGetValue(newString, out original))
    return original;
internDictionary.Add(newString, newString);
return newString;

所以回答你的问题:对==类型变量的object将依次使用ReferenceEquals,将其与上述信息结合起来,以及这两个部分的原因代码表现不同。

您可以在LINQPad中测试以下代码:

void Main()
{
    object o1 = 5;
    object o2 = 5;
    (o1 == o2).Dump("box(5) == box(5)");
    o1.Equals(o2).Dump("box(5).Equals(box(5))");

    object o3 = "5";
    object o4 = "5";
    (o3 == o4).Dump("\"5\" == \"5\"");
    o3.Equals(o4).Dump("\"5\".Equals(\"5\")");

    object o5 = "5";
    object o6 = "15".Substring(1); // "5"
    (o5 == o6).Dump("\"5\" == \"15\".Substring(1)");
    o5.Equals(o6).Dump("\"5\".Equals(\"15\".Substring(1))");

    o6 = string.Intern((string)o6);

    (o5 == o6).Dump("\"5\" == \"15\".Substring(1) [interned]");
    o5.Equals(o6).Dump("\"5\".Equals(\"15\".Substring(1)) [interned]");
}

如您所见,从substring返回的字符串实际执行的操作返回o5已包含的相同引用(在最后结果旁边)。

result of LINQPad script

答案 1 :(得分:1)

根据MSDN

“当前实例与obj参数之间的比较类型取决于当前实例是引用类型还是值类型。如果当前实例是引用类型,则Equals(Object)方法测试引用相等性,调用Equals(Object)方法相当于对ReferenceEquals方法的调用。引用相等意味着被比较的对象变量引用同一个对象。“

“如果当前实例是值类型,则Equals(Object)方法将测试值的相等性。”

对于值类型,它检查两个对象是否属于同一类型,以及两个对象的公共字段和私有字段的值是否相等。

比较上面的两个字符串时,它们都使用相同的内存位置,而a和b对象是引用类型,因此当您比较对象时它们将是不同的对象,但是当您比较它们时它们将具有相同的值值,因为Equals的工作原理。