我有一个非常奇怪的情况,我不明白.以下是简化的案例:
double? d = 2;
int? i = 2;
Console.WriteLine(d.Equals((2))); // false
Console.WriteLine(i.Equals((2))); // true
Run Code Online (Sandbox Code Playgroud)
我不明白为什么一个表达式会使我真实而另一个表达错误.它们似乎相同.
Eri*_*ert 14
你发现这令人困惑是完全正确的.这是一团糟.
让我们首先通过查看更多示例清楚地说明会发生什么,然后我们将推断出正在应用的正确规则.让我们扩展您的程序以考虑所有这些情况:
double d = 2;
double? nd = d;
int i = 2;
int? ni = i;
Console.WriteLine(d == d);
Console.WriteLine(d == nd);
Console.WriteLine(d == i);
Console.WriteLine(d == ni);
Console.WriteLine(nd == d);
Console.WriteLine(nd == nd);
Console.WriteLine(nd == i);
Console.WriteLine(nd == ni);
Console.WriteLine(i == d);
Console.WriteLine(i == nd);
Console.WriteLine(i == i);
Console.WriteLine(i == ni);
Console.WriteLine(ni == d);
Console.WriteLine(ni == nd);
Console.WriteLine(ni == i);
Console.WriteLine(ni == ni);
Console.WriteLine(d.Equals(d));
Console.WriteLine(d.Equals(nd));
Console.WriteLine(d.Equals(i));
Console.WriteLine(d.Equals(ni)); // False
Console.WriteLine(nd.Equals(d));
Console.WriteLine(nd.Equals(nd));
Console.WriteLine(nd.Equals(i)); // False
Console.WriteLine(nd.Equals(ni)); // False
Console.WriteLine(i.Equals(d)); // False
Console.WriteLine(i.Equals(nd)); // False
Console.WriteLine(i.Equals(i));
Console.WriteLine(i.Equals(ni));
Console.WriteLine(ni.Equals(d)); // False
Console.WriteLine(ni.Equals(nd)); // False
Console.WriteLine(ni.Equals(i));
Console.WriteLine(ni.Equals(ni));
Run Code Online (Sandbox Code Playgroud)
所有这些打印都是True,除了那些我记录为打印错误的.
我现在将分析这些案例.
首先要注意的是==操作员总是说True.这是为什么?
非可空的语义==如下:
int == int -- compare the integers
int == double -- convert the int to double, compare the doubles
double == int -- same
double == double -- compare the doubles
Run Code Online (Sandbox Code Playgroud)
所以在每个非可空的情况下,整数2等于double 2.0,因为int 2被转换为double 2.0,并且比较为真.
可空的语义==是:
因此,我们再次看到,可为空的比较,int? == double?,int? == double,等等,我们总是退回到非空的情况下,转换int?到double,做双打比较.因此,这些都是正确的.
现在我们来到Equals,这是事情搞砸的地方.
这里有一个基本的设计问题,我在2009年写过:https://blogs.msdn.microsoft.com/ericlippert/2009/04/09/double-your-dispatch-double-your-fun/ - the问题是==基于两个操作数的编译时类型来解决其含义.但Equals解决的基础上运行时类型的的左操作数(接收器),但编译时间类型的的右侧操作数(参数),这就是为什么事情出轨.
让我们先看一下是double.Equals(object)做什么的.如果调用的接收者Equals(object)是,double那么如果参数不是盒装双,则认为它们不相等.也就是说,Equals要求类型匹配,而==要求类型可以转换为通用类型.
我会再说一遍.double.Equals并没有尝试转换它的参数翻一番,不像==.它只是检查它是否已经是双倍,如果不是,那么它说它们不相等.
那就解释了为什么d.Equals(i)是假的......但......等一下,这不是假的!这解释了什么?
double.Equals超载了!上面我们实际上正在调用double.Equals(double),你猜对了 - 在调用之前将int转换为double!如果我们说过 d.Equals((object)i)),那将是错误的.
好吧,所以我们知道为什么double.Equals(int)是真的 - 因为int被转换为double.
我们也知道为什么double.Equals(int?)是假的. int?是不可兑换成双倍的,但它是object.所以我们打电话double.Equals(object)和打包int,现在它不相等.
怎么样nd.Equals(object)?其语义是:
d.Equals(object)所以现在我们知道为什么nd.Equals(x)有效,如果x是,double或者double?不是,如果它是int或int?.(虽然有趣的是,当然(default(double?)).Equals(default(int?))它们都是真的,因为它们都是空的!)
最后,通过类似的逻辑,我们看到为什么int.Equals(object)给出它所具有的行为.它检查它的参数是否为盒装int,如果不是,则返回false.因此i.Equals(d)是错误的.在i不能转换到加倍,并且d不能被转换为int.
这是一个巨大的混乱.我们希望平等是一种等价关系,而不是!相等关系应该具有以下属性:
==在C#中是正确的,但A.Equals(B)正如我们所见.所以,它在各个层面都是一团糟.==并且Equals有不同的调度机制并给出不同的结果,它们都不是等价关系,而且它总是令人困惑.让你陷入困境的道歉,但当我到达时,它是一团糟.
对于为什么C#中的平等性很糟糕,请参阅我遗憾的语言决策列表中的第9项,这里:http://www.informit.com/articles/article.aspx?p = 2425867
奖励练习:重复上述分析,但适用x?.Equals(y)于可以x为空的情况.什么时候得到与非可空接收器相同的结果,何时得到不同的结果?
| 归档时间: |
|
| 查看次数: |
541 次 |
| 最近记录: |