为什么将相同的两个数字强制转换为对象后它们不相等?

4
我有以下代码片段,但输出结果是错误的。
class Program
    {
        static void Main(string[] args)
        {
            var i = 10000;
            var j = 10000;
            Console.WriteLine((object) i == (object) j);


        }
    }

我原本期望的是true,但实际上却得到了false。

11
你得到了正确的输出...但你的期望是错误的。 - Jon Skeet
@Jon Skeet:无论是代码还是注释,你都很棒。 - Mohanavel
5个回答

21
您正在将数字(通过对象转换)装箱,这将为每个变量创建一个新实例。 对象的 == 运算符基于对象标识(也称为引用相等性),因此您会看到 false (因为实例不同)。
要正确比较这些对象,您可以使用 Object.Equals(i,j) i.Equals(j)。 这将起作用,因为对象的实际运行时实例将是 Int32 ,其 Equals()方法具有整数的正确平等语义。

1
@geek:你的代码有一个bug。对于通用的“object”实例,==的行为是有意义的(因为你不知道你正在比较的东西的任何信息,所以没有其他选择),而且你明确要求使用对象。 - user395760
1
如果你期望得到true作为结果,但实际上没有得到,我们可以说这是你代码中的一个bug;-) - BrokenGlass
1
@geek 不,这是按照语言定义应该工作的方式。 - Davy8
1
是的,这是一个 bug,在你的代码中。你只需要输入 Console.WriteLine(i == j); 就能得到正确的答案。要获取更多信息,请查阅 .NET 中值类型和引用类型之间的区别。简而言之,通过转换为对象,你将 i 和 j 的值放入内存“堆”中的引用中,然后测试看它们是否指向堆上的同一位置(事实并非如此)。 - KeithS
1
@RQDQ:如果你没有任何有建设性的话要说,就不要说了。你只会制造噪音。 - capdragon
显示剩余3条评论

5

您正在使用默认调用ReferenceEquals==。 您应该使用:

Console.WriteLine(((object)i).Equals((object)j));

2
因为你正在比较两个不同的对象,这就是原因。

1

运算符是被重载而不是被覆盖的。这意味着实际调用的方法在编译时确定,而不是在运行时确定。为了理解这种差异,您应该像这样考虑:

假设您有以下代码

public class MyBase
{
    public virtual void DoSomething() { Console.WriteLine("Base"); }
}

public class A : MyBase
{
    public override void DoSomething() { Console.WriteLine("A"); }
}

public class B : MyBase
{
    public override void DoSomething() { Console.WriteLine("B"); }
}

现在你可以创建 A 和 B 的实例:

MyBase a = new A();
MyBase b = new B();

a.DoSomething(); // prints "A"
b.DoSomething(); // prints "B"

发生的情况是每个 MyBase 实例都包含一个指向必须调用的 DoSomething() 实际实现的 vtable 的隐藏引用。因此,当运行时需要在 a 上调用 DoSomething() 时,它将查看 a 的 vtable,并找到打印 "A" 的版本。重要的部分是这发生在运行时。

现在,当您重载方法时,您创建一个具有相同名称但不同签名的方法。

public class C
{
    public void Print(string str) { Console.WriteLine("string: " + string); }
    public void Print(int i) { Console.WriteLine("int: " + i); }
    public void Print(object obj) { Console.WriteLine("object: " + obj.ToString()); }
}

var c = new C();
c.Print("1"); // string: 1
c.Print(1); // int: 1
c.Print((object)1); // object: 1

这次编译器决定调用哪个方法,而不是在运行时。但编译器并不是很聪明。当它看到 (object)1 时,它看到的是一个对象的引用,并尝试找到一个可以接受对象作为参数的 Print() 版本。

有时编译器会找到两个可用的版本。例如,"1" 是一个字符串,但它也是一个对象,因此第一个和第三个版本的 Print() 都能够接受一个字符串。在这种情况下,编译器选择具有最具体参数类型的版本。有时编译器无法选择,就像以下示例中一样:

public static void Print(string a, object b) { Console.WriteLine("V1"); }
public static void Print(object a, string b) { Console.WriteLine("V2"); }

Print("a", "b"); // does this print "V1" or "V2"?

在这种情况下,没有一个版本比另一个更具体,因此编译器会产生编译错误:调用以下方法或属性时存在歧义:'Test.Program.Print(string, object)' 和 'Test.Program.Print(object, string)' 正如我在开头所说的,== 运算符被重载了。这意味着编译器会选择使用哪个版本。最通用的版本是两个操作数都是 Object 类型的版本。这个版本比较引用。这就是为什么 (object) i == (object) j 返回 false 的原因。

1

它们是两个不同的对象,永远不会相等。它们的值是相等的。


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