对于 C # 中的原语,= = 和 Equals ()有什么区别?

考虑下面的代码:

int age = 25;
short newAge = 25;
Console.WriteLine(age == newAge);  //true
Console.WriteLine(newAge.Equals(age)); //false
Console.ReadLine();

intshort都是基元类型,但与 ==的比较返回 true,与 Equals的比较返回 false。

为什么?

18212 次浏览

简短的回答:

平等是很复杂的。

详细回答:

基元类型覆盖基 object.Equals(object),如果装箱的 object具有相同的 类型和值,则返回 true。(注意,它也适用于可空类型; 非空可空类型总是对基础类型的实例使用方框。)

因为 newAgeshort,所以它的 Equals(object)方法只有在传递具有相同值的装箱的 太短了时才返回 true。您传递的是一个方框 int,因此它返回 false。

相比之下,==操作符被定义为获取两个 int(或 shortlong)。
当您使用 intshort调用它时,编译器将隐式地将 short转换为 int,并按值比较得到的 int

还有其他办法

基元类型还有它们自己的接受相同类型的 Equals()方法。
如果编写 age.Equals(newAge),编译器将选择 int.Equals(int)作为最佳重载,并隐式地将 short转换为 int。然后它将返回 true,因为这个方法只是直接比较 int

short也有一个 short.Equals(short)方法,但是 int不能隐式地转换为 short,所以不能调用它。

您可以通过强制转换强制它调用这个方法:

Console.WriteLine(newAge.Equals((short)age)); // true

这将直接调用 short.Equals(short),不装箱。如果 age大于32767,它将抛出溢出异常。

您也可以调用 short.Equals(object)重载,但是显式地传递一个装箱的对象,以便它得到相同的类型:

Console.WriteLine(newAge.Equals((object)(short)age)); // true

与前面的替代方案一样,如果它不适合 short,那么它将抛出溢出。 与以前的解决方案不同,它将 short装箱成一个对象,浪费时间和内存。

源代码:

下面是来自实际源代码的两个 Equals()方法:

    public override bool Equals(Object obj) {
if (!(obj is Int16)) {
return false;
}
return m_value == ((Int16)obj).m_value;
}


public bool Equals(Int16 obj)
{
return m_value == obj;
}

进一步阅读:

参见 Eric Lippert

因为接受 intshort.Equals没有重载,所以称为:

public override bool Equals(object obj)
{
return obj is short && this == (short)obj;
}

obj不是 short. . 因此,它是假的。

对于值类型,.Equals要求两个对象具有相同的类型和相同的值,而 ==只是测试两个值是否相同。

Object.Equals
Http://msdn.microsoft.com/en-us/library/bsc2ak47(v=vs.110).aspx

当你把 int传给 short的等式时,你就传给了 object:

enter image description here 因此,这个伪代码运行:

return obj is short && this == (short)obj;

= = 原始

Console.WriteLine(age == newAge);          // true

在原语比较中 = = 运算符的行为非常明显,在 C # 中有许多 = = 运算符重载可用。

  • 字符串 = = 字符串
  • = = int
  • = = uint
  • 长 = = 长
  • 更多

所以在这种情况下,没有从 intshort的隐式转换,但是 shortint是可能的。所以 newAge 被转换为 int 并进行比较,当两者保持相同的值时返回 true。所以它相当于:

Console.WriteLine(age == (int)newAge);          // true

。在原语中等于()

Console.WriteLine(newAge.Equals(age));         //false

这里我们需要了解 Equals ()方法是什么,我们使用一个短的类型变量调用 Equals。因此有三种可能性:

  • 等于(对象,对象)//来自对象的静态方法
  • 等于(对象)//来自对象的虚方法
  • 等于(短)//实现 IEquable. 等于(短)

第一个类型在这里不是大小写,因为我们只调用一个 int 类型的参数时,参数的数量是不同的。第三个也被消除,因为上面提到的将 int 隐式转换为 short 是不可能的。所以这里第二种类型的 Equals(object)被称为。short.Equals(object)是:

bool Equals(object z)
{
return z is short && (short)z == this;
}

所以这里的条件测试了 z is short,它是 false,因为 z 是一个 int,所以返回 false。

以下是 Eric Lippert 的详细文章

您需要认识到的是,执行 ==最终总是会调用一个方法。问题是调用 ==Equals是否最终会调用/执行相同的事情。

对于引用类型,==总是首先检查引用是否相同(Object.ReferenceEquals)。另一方面,Equals可以被重写,并且可以检查某些值是否相等。

编辑: 回答 svick 并添加 SLaks 注释,这里是一些 IL 代码

int i1 = 0x22; // ldc.i4.s ie pushes an int32 on the stack
int i2 = 0x33; // ldc.i4.s
short s1 = 0x11; // ldc.i4.s (same as for int32)
short s2 = 0x22; // ldc.i4.s


s1 == i1 // ceq
i1 == s1 // ceq
i1 == i2 // ceq
s1 == s2 // ceq
// no difference between int and short for those 4 cases,
// anyway the shorts are pushed as integers.


i1.Equals(i2) // calls System.Int32.Equals
s1.Equals(s2) // calls System.Int16.Equals
i1.Equals(s1) // calls System.Int32.Equals: s1 is considered as an integer
// - again it was pushed as such on the stack)
s1.Equals(i1) // boxes the int32 then calls System.Int16.Equals
// - int16 has 2 Equals methods: one for in16 and one for Object.
// Casting an int32 into an int16 is not safe, so the Object overload
// must be used instead.

Equals () 系统目标类的一种方法
语法: < strong > Public Virtual bool Equals ()
建议如果我们想要比较两个对象的状态,那么我们应该使用 等于()方法

如上所述的答案 ==运算符比较的值是相同的。

请不要和 Referenceequals 混淆

引用等于()
语法: public static bool ReferenceEquals ()
它确定指定的对象实例是否属于同一实例

==用于检查等于条件,它可以被看作一个操作符(布尔操作符) ,只是比较两件事情,在这里数据类型并不重要,因为会有一个类型强制转换,Equals也用于检查等于条件,但在这种情况下,数据类型应该是相同的。N Equals 是一个方法而不是一个运算符。

下面是从您提供的一个小例子,这将澄清差异简要,。

int x=1;
short y=1;
x==y;//true
y.Equals(x);//false

在上面的例子中,X 和 Y 具有相同的值,即1,当我们使用 ==时,它将返回 true,就像在 ==中一样,短类型被编译器转换为 int 并给出结果。

当我们使用 Equals时,比较已经完成,但是类型转换不是由编译器完成的,因此返回 false。

伙计们,如果我错了请告诉我。

在许多方法或运算符参数不属于所需类型的上下文中,C # 编译器将尝试执行隐式类型转换。如果编译器可以通过添加隐式转换使所有参数满足其运算符和方法,那么它将毫无怨言地这样做,即使在某些情况下(特别是在相等性测试中!)结果可能令人惊讶。

而且,每种值类型(如 intshort)实际上都描述了一种值和一种对象(*)。存在隐式转换,可以将值转换为其他类型的值,也可以将任何类型的值转换为相应类型的对象,但不同类型的对象之间并不存在隐式转换。

如果使用 ==运算符比较 shortint,则 short将隐式转换为 int。如果它的数值等于 int,那么它被转换的 int将等于它被比较的 int。如果一个人试图使用 Equals方法在短期比较它与一个 int,但是,唯一的隐式转换将满足一个重载的 Equals方法将是转换到对应于 int的对象类型。当询问 short是否匹配传入的对象时,它将观察到所涉及的对象是 int而不是 short,从而得出它不可能相等的结论。

一般来说,尽管编译器不会抱怨,但是应该避免比较不同类型的事物; 如果你想知道将事物转换成一种通用形式是否会得到相同的结果,那么你应该显式地执行这种转换。例如,考虑一下,

int i = 16777217;
float f = 16777216.0f;


Console.WriteLine("{0}", i==f);

有三种方法可以用来比较 intfloat。有人可能想知道:

  1. int最接近的 float值是否与 float相匹配?
  2. float的整数部分与 int匹配吗?
  3. intfloat是否表示相同的数值。

如果试图直接比较 intfloat,那么编译后的代码将回答第一个问题; 然而,这是否是程序员的意图还远不明显。将比较改为 (float)i == f可以清楚地表明第一个意思是有意的,或者 (double)i == (double)f可以使代码回答第三个问题(并且清楚地表明这就是有意的)。

(*)即使 C # 规范将类型值(例如 System.Int32)视为类型 System.Int32的对象,这种观点也与规范将值和对象视为居住在不同宇宙中的平台上运行的代码的要求相矛盾。此外,如果 T是引用类型,而 xT,那么 T类型的引用应该能够引用 x。因此,如果 Int32类型的变量 v持有 Object,则 Object类型的引用应该能够持有对 v或其内容的引用。实际上,类型为 Object的引用能够指向一个包含从 v复制的数据的对象,但不能指向 v本身或其内容。这意味着 v和它的内容都不是真正的 Object