为什么整数零不等于长零

本文关键字:不等于 为什么 整数 | 更新日期: 2023-09-27 18:25:32

我刚刚在 C# 中发现的一段奇怪的代码(对于使用 .NET的structs(。

using System;
public class Program
{
    public static void Main(string[] args)
    {
    int a;
    long b;
    a = 0;
    b = 0;
    Console.WriteLine(a.Equals(b)); // False
    Console.WriteLine(a.Equals(0L)); // False
    Console.WriteLine(a.Equals((long)0)); // False
    Console.WriteLine(a.Equals(0)); // True
    Console.WriteLine(a.Equals(a)); // True
    Console.WriteLine(a == b); // True
    Console.WriteLine(a == 0L); // True
    Console.WriteLine();
    Console.WriteLine(b.Equals(a)); // True
    Console.WriteLine(b.Equals(0)); // True
    Console.WriteLine(b.Equals((int)0)); // True
    Console.WriteLine(b.Equals(b)); // True
    Console.WriteLine(b == a); // True
    Console.WriteLine(b == 0); // True
    }
}

这里有两个有趣的点(假设aint的,blong(:

  1. a != b,但b == a;
  2. (a.Equals(b)) != (a == b)

这种方式实施比较有什么原因吗?

注意:如果有任何区别,则使用 .NET 4。

为什么整数零不等于长零

通常,对于不同类型的对象,Equals()方法不应返回 true。

a.Equals(b)调用 int.Equals(object) ,这只能对盒装Int32 s 返回 true:

public override bool Equals(Object obj) { 
    if (!(obj is Int32)) {
        return false;
    }
    return m_value == ((Int32)obj).m_value; 
}  
b.Equals(a)在将

int隐式转换为long后调用long.Equals(long)
因此,它直接比较两个long,返回 true。

为了更清楚地理解,请查看这个更简单的示例(打印 True False True(生成的 IL:

int a = 0;
long b = 0L;
Console.WriteLine(a == b);
Console.WriteLine(a.Equals(b));
Console.WriteLine(b.Equals(a));

IL_0000:  ldc.i4.0    
IL_0001:  stloc.0     
IL_0002:  ldc.i4.0    
IL_0003:  conv.i8     
IL_0004:  stloc.1     
IL_0005:  ldloc.0     //Load a
IL_0006:  conv.i8     //Cast to long
IL_0007:  ldloc.1     //Load b
IL_0008:  ceq         //Native long equality check
IL_000A:  call        System.Console.WriteLine    //True
IL_000F:  ldloca.s    00            //Load the address of a to call a method on it
IL_0011:  ldloc.1                   //Load b
IL_0012:  box         System.Int64  //Box b to an Int64 Reference
IL_0017:  call        System.Int32.Equals
IL_001C:  call        System.Console.WriteLine    //False
IL_0021:  ldloca.s    01  //Load the address of b to call a method on it
IL_0023:  ldloc.0         //Load a
IL_0024:  conv.i8         //Convert a to Int64
IL_0025:  call        System.Int64.Equals
IL_002A:  call        System.Console.WriteLine    //True

它们不一样,因为即使是简单的类型也是从 System.Object 继承的 - 它们实际上是对象,不同的对象类型,即使具有相同的属性值也不相等。

例:

您可以有一个只有一个属性的 Co-Worker 对象:名称(字符串(和一个只有一个属性的合作伙伴对象:名称(字符串(

同事大卫和帕纳大卫不一样。 它们是不同的对象类型这一事实使它们与众不同。

在您的情况下,请使用 .Equals((,你不是在比较值,而是在比较对象。该对象不是"0",它是一个值为零的 System.Int32 和一个值为零的 System.Int64。

基于以下注释中的问题的代码示例:

class CoWorker
{
   public string Name { get; set; }
}
class Partner
{
   public string Name { get; set; }
}
private void button1_Click(object sender, RoutedEventArgs e)
{
   CoWorker cw = new CoWorker();
   cw.Name = "David Stratton";
   Partner p = new Partner();
   p.Name = "David Stratton";
   label1.Content = cw.Equals(p).ToString();  // sets the Content to "false"
}

运算符和方法重载以及转换运算符在编译时计算,这与在运行时计算的虚拟方法覆盖不同。 表达式someIntVar.Equals(someNumericQuantity)与表达式someObjectVarThatHoldsAnInt.Equals(someNumericQuantity)完全无关。 如果你假装虚拟方法Object.Equals有一个不同的名称(如IsEquivalentTo(,并在使用虚拟方法的每个地方替换该名称,这将更加清晰。 整数零可能在数值上等于长零,但这并不意味着它们在语义上是等效的。

顺便提一下,EqualsIsEquivalentTo之间的这种含义分离也有助于避免后者定义中的模糊性。 可以为任意对象定义有意义的等价关系:存储位置X应被视为等效于存储位置Y,如果前者的所有成员的行为始终等效于后者的相应成员,并且确定XY是否引用同一对象的唯一方法是使用 Reflection 或 ReferenceEquals 。 即使1.0m.Equals(1.00m)是真的,也应该是真的,1.0m.IsEquivalentTo(1.00m)应该是假的。 不幸的是,对象等价性测试方法和Decimal数值相等性测试方法使用相同的名称导致Microsoft定义前者的行为类似于后者。

还存在缩小或扩大转换的问题。long零总是等于int零,但反之则不然。

当将长整型与整数进行比较时,仅比较最低有效的 32 位,其余的被忽略,因此即使较低的位匹配,int.Equals(long)运算也无法保证相等。

int a = 0;
long b = 0;
Trace.Assert(a.Equals((int)b));     // True   32bits compared to 32bits
Trace.Assert(a.Equals((long)b));    // False  32bits compared to 64bits (widening)
Trace.Assert(b.Equals((long)a));    // True   64bits compared to 64bits
Trace.Assert(b.Equals((int)a));     // True   64bits compared to 32bits (narrowing)

还要考虑以下情况:较低的 32 位相等,但较高的 32 位不相等。

uint a = 0;
ulong b = 0xFFFFFF000000;
Trace.Assert((uint)a == (uint)b);  // true because of a narrowing conversion
Trace.Assert((ulong)a == (ulong)b);  // false because of a widening conversion

因为 Equals 比较对象,而 a 和 b 对象不同。它们具有相同的值,但作为对象不同

此链接可以帮助您:http://msdn.microsoft.com/en-us/library/ms173147(v=vs.80(.aspx

C# 不执行自动强制转换。等于函数比较类型和值。很像 JS中的===。