使用.Equals()方法和==运算符进行比较,以及基元类型和对象的混淆问题

3

请考虑以下代码:

int a = 0;
short b = 0;
int c = 0;
object a1 = a;
object b1 = b;
object c1 = c;

Console.WriteLine(1);
//comparing primitives - int vs. short
Console.WriteLine(a == b);
Console.WriteLine(b == a);
Console.WriteLine(a.Equals(b));
Console.WriteLine(b.Equals(a));

Console.WriteLine(2);
//comparing objects - int vs. int
Console.WriteLine(c1 == a1);
Console.WriteLine(a1 == c1);
Console.WriteLine(c1.Equals(a1));
Console.WriteLine(a1.Equals(c1));

Console.WriteLine(3);
//comparing objects - int vs. short
Console.WriteLine(a1 == b1);
Console.WriteLine(b1 == a1);
Console.WriteLine(a1.Equals(b1)); //???
Console.WriteLine(b1.Equals(a1));

它会打印出以下输出:
1
True
True
True
False
2
False
False
True
True
3
False
False
False
False

我知道的;明确的事情

第二部分:当使用==运算符与对象一起使用时,如果比较两个不同名称引用的内存中的一个对象(不太频繁,但可能会发生),则返回true。 Equals()方法比较对象的内容(值)。在这个网站的许多答案中提到了这一点。

第一部分:使用==运算符,编译器将“小”的类型转换为“大”的类型(例如:short 转换成 int)并比较原始值。操作数的顺序(变量)无关紧要。最后一行的Equals()的结果可能令人困惑,因为它返回false(不比较值),但是这是可以理解的。顺序很重要。正如在这个答案中所学到的那样,必须选择最佳的重载。它是由第一个变量的类型来选择的:short.Equals(short)。但是,int不能转换为“小”的类型(short),因此没有进行比较,该方法返回false。

问题:

  1. 我上面的理解是正确的吗?
  2. 为什么第三部分的最后两行(使用Equals())都返回false?与第一部分的第3行有什么不同?为什么不进行重载和值比较?这变得很抽象,我找不到原因。

2
它们返回 false 是因为你在 Object 中 装箱 了值类型 int。这不是同一个实例(Object.Equals 使用 Object.ReferenceEquals),所以 Equals 返回 false。当值类型被装箱时,会分配并构造一个新对象。 - Tim Schmelter
@TimSchmelter 注意 (b.Equals(a)); 会给你 false,而且没有装箱。 - BRAHIM Kamel
@TimSchmelter 我认为这不是真的,因为object.Equals是虚方法,所以在OP的情况下将调用int.Equals而不是object.Equals - Evk
@BRAHIMKamel:b.Equals(a) 返回 false 的原因是 int16.Equals 如果类型不能隐式转换为 short,则调用 Equals(Object)Int32 不是可隐式转换的类型,因此返回 false。如果你反转条件,你会得到 true,因为 short 可以隐式转换为 int。阅读:https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/implicit-numeric-conversions-table - Tim Schmelter
@TimSchmelter 谢谢,我知道,但请看一下我的答案 :) - BRAHIM Kamel
显示剩余2条评论
2个回答

2
在第一部分第三行:
int a = 0;
short b = 0;
Console.WriteLine(a.Equals(b));

您调用的是 int 的重载函数:bool Equals(int other),因为 b(短整型)可以隐式转换为 int,所以选择了这个重载。它返回 true。在第三部分第三行。
int a = 0;
short b = 0;
object a1 = a;
object b1 = b;
Console.WriteLine(a1.Equals(b1)); //???

另外一个 int 的重载(不是 object,因为 Equals 是虚方法)被调用了:bool Equals(object other)。为了返回 true,other 应该是完全相同的类型 (int),但实际上它是 short,所以它返回 false。装箱在这里并不相关,您可以通过以下方式进行验证:

int a = 0;            
int c = 0;
object a1 = a;
object c1 = c;
// yes, different objects
Console.WriteLine(a1 == c1); // false
// still equal, because both are boxed ints
Console.WriteLine(a1.Equals(c1)); // true

关于理解,我认为文档包含了所有相关信息。只需记住以下几点:
  1. ==运算符和Equals方法都可以在类中手动定义,因此理论上可以做任何事情。您的理解仅涉及“默认”行为。

  2. ==在常规意义上不是虚拟的,不像Equals方法。所以当你执行a1 == b1时 - ==会在编译时定义(基于a1b1的类型),但当你调用a1.Equals(b1)时 - 它是虚拟分派的,所以要调用的方法在运行时定义。


1

除了 boxing,这意味着值类型将在内存中导致不同的引用之外,您还必须考虑 隐式数字转换。实际上,正是因为这个原因,你才有

Console.WriteLine(a.Equals(b)); 

这个给你一个true,但不是这个

Console.WriteLine(b.Equals(a));

这是另一个例子。
static void Main(string[] args)
        {


            int i = 0;
            long L = 0;


            Console.WriteLine(1);
            //comparing primitives - int vs. short

            Console.WriteLine(L.Equals(i)); //true
            Console.WriteLine(i.Equals(L));//false
        }

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