C# 为什么long和decimal之间的Equals是不可交换的?
我在linqpad中运行了以下代码:C# 为什么long和decimal之间的Equals是不可交换的?,c#,.net,C#,.net,我在linqpad中运行了以下代码: long x = long.MaxValue; decimal y = x; x.Dump(); y.Dump(); (x == y).Dump(); (y == x).Dump(); Object.Equals(x, y).Dump(); Object.Equals(y, x).Dump(); x.Equals(y).Dump(); y.Equals(x).Dump();
long x = long.MaxValue;
decimal y = x;
x.Dump();
y.Dump();
(x == y).Dump();
(y == x).Dump();
Object.Equals(x, y).Dump();
Object.Equals(y, x).Dump();
x.Equals(y).Dump();
y.Equals(x).Dump();
它产生以下输出:
9223372036854775807
9223372036854775807
True
True
False
False
False
True
注意最后两行:x.Equals(y)为假,但y.Equals(x)为真。因此,十进制认为自己等于一个具有相同值的长,但长并不认为它本身等于具有相同值的十进制。
这种行为的解释是什么
更新:
我接受了李的回答
我对此很好奇,写了一个小程序:
using System;
namespace TestConversion
{
class Program
{
static void Main(string[] args)
{
long x = long.MaxValue;
decimal y = x;
Console.WriteLine(x);
Console.WriteLine(y);
Console.WriteLine(x == y);
Console.WriteLine(y == x);
Console.WriteLine(Object.Equals(x, y));
Console.WriteLine(Object.Equals(y, x));
Console.WriteLine(x.Equals(y));
Console.WriteLine(y.Equals(x));
Console.ReadKey();
}
}
}
然后在IL中对其进行分解:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] int64 x,
[1] valuetype [mscorlib]System.Decimal y)
L_0000: nop
L_0001: ldc.i8 9223372036854775807
L_000a: stloc.0
L_000b: ldloc.0
L_000c: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(int64)
L_0011: stloc.1
L_0012: ldloc.0
L_0013: call void [mscorlib]System.Console::WriteLine(int64)
L_0018: nop
L_0019: ldloc.1
L_001a: call void [mscorlib]System.Console::WriteLine(valuetype [mscorlib]System.Decimal)
L_001f: nop
L_0020: ldloc.0
L_0021: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(int64)
L_0026: ldloc.1
L_0027: call bool [mscorlib]System.Decimal::op_Equality(valuetype [mscorlib]System.Decimal, valuetype [mscorlib]System.Decimal)
L_002c: call void [mscorlib]System.Console::WriteLine(bool)
L_0031: nop
L_0032: ldloc.1
L_0033: ldloc.0
L_0034: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(int64)
L_0039: call bool [mscorlib]System.Decimal::op_Equality(valuetype [mscorlib]System.Decimal, valuetype [mscorlib]System.Decimal)
L_003e: call void [mscorlib]System.Console::WriteLine(bool)
L_0043: nop
L_0044: ldloc.0
L_0045: box int64
L_004a: ldloc.1
L_004b: box [mscorlib]System.Decimal
L_0050: call bool [mscorlib]System.Object::Equals(object, object)
L_0055: call void [mscorlib]System.Console::WriteLine(bool)
L_005a: nop
L_005b: ldloc.1
L_005c: box [mscorlib]System.Decimal
L_0061: ldloc.0
L_0062: box int64
L_0067: call bool [mscorlib]System.Object::Equals(object, object)
L_006c: call void [mscorlib]System.Console::WriteLine(bool)
L_0071: nop
L_0072: ldloca.s x
L_0074: ldloc.1
L_0075: box [mscorlib]System.Decimal
L_007a: call instance bool [mscorlib]System.Int64::Equals(object)
L_007f: call void [mscorlib]System.Console::WriteLine(bool)
L_0084: nop
L_0085: ldloca.s y
L_0087: ldloc.0
L_0088: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(int64)
L_008d: call instance bool [mscorlib]System.Decimal::Equals(valuetype [mscorlib]System.Decimal)
L_0092: call void [mscorlib]System.Console::WriteLine(bool)
L_0097: nop
L_0098: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
L_009d: pop
L_009e: ret
}
您确实可以看到long值被转换为decimal
谢谢你们 您正在比较对象引用和值。当然,这些引用并不相同——除了在第2行中显式设置的引用。但是,这些值是
C#自动为您管理指针(例如“引用内存”)。您正在访问一个从语法上看不明显的层。这就是C#隐式与显式转换的本质 发件人: 隐式转换:不需要特殊语法,因为 转换是类型安全的,不会丢失任何数据。例子包括 从较小整数类型到较大整数类型的转换,以及 从派生类到基类 显式转换(强制转换):显式转换需要强制转换 操作人员当信息可能丢失时,需要强制转换 转换,或者在其他系统的转换可能无法成功时 原因。典型示例包括将数值转换为 具有较低的精度或较小的范围,以及基类的转换 一个派生类的实例
long可以很容易地转换为十进制,但反之则不正确,因此计算失败。这是因为
y.Equals(x);
正在调用decimal.Equals(decimal)
重载,因为long
和decimal
之间存在一个中间值。结果,比较返回true
但是,由于没有从decimal
到long
x.Equals(y)
调用
long.Equals(object)
,这会导致y
被装箱,并且比较返回false
,因为它不能被解装箱为long。fyi,将小数点转换回long works:x.Equals((long)y).Dump()代码>虽然这并不能确切解释这种行为,但您可能想看看这个:@leppie:在实际的程序中,我们不会比较它们。我们通过做一些调试发现了这一点,我发现它很奇怪——因此我的帖子。“科斯塔考虑更新标题来明确地讨论长/小数比较……”科斯塔,你还在问平等是否应该交流?如果是这样,yesequals应该是交际性和及物性的。但这只是一个指导方针。以前我自己也有理由违反这一准则。但在这种情况下,关于长和十进制的比较,这听起来确实像是MS搞砸了。Equals应该检查值是否相等。另外,请参阅其中明确说明Equals的实现应保证x.Equals(y)和y.Equals(x)返回相同的值。如果指针不是相同的对象,它们都是值类型。Equals将失败,因为对象确实不相等。这里不叫Object.equals。这个答案加上来自其他答案的链接-给出了完整的解释。这个答案很奇怪,但不知何故还是感觉正确。。。你是说虽然decimal知道long,但long不知道decimal(在转换方面)?你看到的是错误的转换<代码>1.1
不能用长
表示。因此,从decimal
到long
的转换必须是明确的,因为这将导致精度损失。从long
到decimal
的转换是隐式的。原因相同。因为没有从ulong
到long
的隐式转换。为什么会这样?因为有些值可以在ulong
中表示,但不能在long
@leppie中表示,所以不会比较位模式。选择Equals(object)
重载,因为没有从ulong
到long
的隐式转换。参数是装箱的,long.Equals(object)
方法必须做的第一件事是查看参数是否是装箱的long来进行比较。此操作失败,因为参数是装箱的ulong
。