为什么整数零不等于长零
本文关键字:不等于 为什么 整数 | 更新日期: 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
}
}
这里有两个有趣的点(假设a
是int
的,b
是long
(:
-
a != b
,但b == a
; -
(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
(,并在使用虚拟方法的每个地方替换该名称,这将更加清晰。 整数零可能在数值上等于长零,但这并不意味着它们在语义上是等效的。
顺便提一下,Equals
和IsEquivalentTo
之间的这种含义分离也有助于避免后者定义中的模糊性。 可以为任意对象定义有意义的等价关系:存储位置X
应被视为等效于存储位置Y
,如果前者的所有成员的行为始终等效于后者的相应成员,并且确定X
和Y
是否引用同一对象的唯一方法是使用 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中的===。