double 和有什么不一样?和国际?对于 .Equals 比较? [英] What is the difference between double? and int? for .Equals comparisons?

查看:48
本文介绍了double 和有什么不一样?和国际?对于 .Equals 比较?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个非常奇怪的情况,我不明白.下面是简化的案例:

I have a very strange situation I don't understand. Below is the case simplified:

double? d = 2;
int? i = 2;

Console.WriteLine(d.Equals((2))); // false
Console.WriteLine(i.Equals((2))); // true

我不明白为什么一个表达会让我觉得是真的,而另一个是假的.它们看起来完全一样.

I don't understand why one expression will net me true and another false. They seem identical.

推荐答案

您认为这令人困惑是完全正确的.一团糟.

You are completely right to find this confusing. It is a mess.

让我们首先通过查看更多示例清楚地说明会发生什么,然后我们将推导出此处应用的正确规则.让我们扩展您的程序以考虑所有这些情况:

Let's start by clearly saying what happens by looking at more examples, and then we will deduce the correct rules that are being applied here. Let's extend your program to consider all these cases:

    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));

除了我标记为打印错误的那些之外,所有这些都打印为 True.

All of those print True except the ones I have notated as printing false.

我现在将对这些案例进行分析.

I'll now give an analysis of those cases.

首先要注意的是 == 运算符总是表示 True.这是为什么?

The first thing to notice is that the == operator always says True. Why is that?

不可为空的==的语义如下:

The semantics of non-nullable == are as follows:

int == int -- compare the integers
int == double -- convert the int to double, compare the doubles
double == int -- same
double == double -- compare the doubles

所以在每个不可为空的情况下,整数 2 等于 double 2.0,因为 int 2 被转换为 double 2.0,并且比较结果为真.

So in every non-nullable case, integer 2 is equal to double 2.0 because the int 2 is converted to double 2.0, and the comparison is true.

可空==的语义是:

  • 如果两个操作数都为空,则它们相等
  • 如果一个为空而另一个不是,则它们不相等
  • 如果两者都不为空,则回退到上述不可为空的情况.

再一次,我们看到对于可空比较,int?== double?, int?== double,以此类推,我们总是退回到不可为空的情况,将int?转换为double,并以doubles进行比较.因此这些也都是真的.

So again, we see that for the nullable comparisons, int? == double?, int? == double, and so on, we always fall back to the non-nullable cases, convert the int? to double, and do the comparison in doubles. Thus these are also all true.

现在我们来到Equals,这就是事情变得一团糟的地方.

Now we come to Equals, which is where things get messed up.

这里有一个基本的设计问题,我在 2009 年写过:https://blogs.msdn.microsoft.com/ericlippert/2009/04/09/double-your-dispatch-double-your-fun/ --问题在于 == 的含义是基于 两个操作数的编译时类型 来解决的.但是Equals是根据操作数(接收者)的运行时类型解析的,但是编译时类型right 操作数(参数),这就是事情出轨的原因.

There is a fundamental design problem here, which I wrote about in 2009: https://blogs.msdn.microsoft.com/ericlippert/2009/04/09/double-your-dispatch-double-your-fun/ -- the problem is that the meaning of == is resolved based on the compile time types of both operands. But Equals is resolved on the basis of the run time type of the left operand (the receiver), but the compile time type of the right operand (the argument), and that's why things go off the rails.

让我们先看看 double.Equals(object) 的作用.如果对Equals(object) 的调用的接收者是double,那么如果参数不是装箱双精度数,则认为它们不相等.也就是说,Equals 要求类型匹配,而 == 要求类型可转换为通用类型.

Let's begin by looking at what double.Equals(object) does. If the receiver of a call to Equals(object) is double then if the argument is not a boxed double, they are considered not equal. That is, Equals requires that the types match, whereas == requires that the types be convertible to a common type.

我再说一遍.double.Equals 不会尝试将其参数转换为双精度值,这与 == 不同.它只是检查它是否已经double,如果不是,那么它说它们不相等.

I'll say that again. double.Equals does not try to convert its argument to double, unlike ==. It just checks to see if it already is double, and if it is not, then it says they are not equal.

这就解释了为什么 d.Equals(i) 是假的......但是......等等,上面的不是是假的!这是什么原因?

That then explains why d.Equals(i) is false... but... wait a minute, it is not false above! What explains this?

double.Equals 已重载!上面我们实际上是在调用 double.Equals(double),它——你猜对了——在调用之前将 int 转换为 double!如果我们说 d.Equals((object)i)) 那就错了.

double.Equals is overloaded! Above we are actually calling double.Equals(double), which -- you guessed it -- converts the int to a double before doing the call! If we had said d.Equals((object)i)) then that would be false.

好的,所以我们知道为什么 double.Equals(int) 是真的——因为 int 被转换为 double.

All right, so we know why double.Equals(int) is true -- because the int is converted to double.

我们也知道为什么 double.Equals(int?) 是假的.int? 不能转换为 double,但可以转换为 object.所以我们调用 double.Equals(object) 并将 int 装箱,现在它不相等了.

We also know why double.Equals(int?) is false. int? is not convertible to double, but it is to object. So we call double.Equals(object) and box the int, and now its not equal.

nd.Equals(object) 怎么样?其语义是:

  • 如果接收者为空且参数为空,则它们相等
  • 如果接收者不为空,则遵循 d.Equals(object)
  • 的不可为空的语义
  • If the receiver is null and the argument is null, they are equal
  • If the receiver is not null then defer to the non-nullable semantics of d.Equals(object)

所以现在我们知道为什么 nd.Equals(x)xdoubledouble 时有效? 但如果是 intint? 则不然.(虽然有趣的是,当然 (default(double?)).Equals(default(int?)) 会是真的,因为它们都是空的!)

So now we know why nd.Equals(x) works if x is a double or double? but not if it is int or int?. (Though interestingly, of course (default(double?)).Equals(default(int?)) would be true since they are both null!)

最后,通过类似的逻辑,我们看到了为什么 int.Equals(object) 给出了它所具有的行为.它检查它的参数是否是一个装箱的 int,如果不是,则返回 false.因此 i.Equals(d) 是假的.i不能转为double,d不能转为int.

Finally, by similar logic we see why int.Equals(object) gives the behaviour it has. It checks to see if its argument is a boxed int, and if it is not, then it returns false. Thus i.Equals(d) is false. The i cannot be converted to double, and the d cannot be converted to int.

这真是一团糟.我们希望相等成为等价关系,但事实并非如此!相等关系应具有以下属性:

This is a huge mess. We would like equality to be an equivalence relation, and it is not! An equality relationship should have these properties:

  • 自反性:事物等于自身.这在 C# 中通常是正确的,但也有一些例外.
  • 对称性:如果 A 等于 B,则 B 等于 A.C# 中的 == 是这样,但 A.Equals(B) 不是这样,正如我们所见.
  • 传递性:如果 A 等于 B 且 B 等于 C,则 A 也等于 C.在 C# 中绝对不是这种情况.
  • Reflexivity: a thing is equal to itself. That is usually true in C# though there are a few exceptions.
  • Symmetry: If A is equal to B then B is equal to A. That is true of == in C# but not of A.Equals(B), as we've seen.
  • Transitivity: If A equals B and B equals C then A also equals C. That is absolutely not the case in C#.

所以,它在各个层面上都是一团糟.==Equals 有不同的调度机制,给出的结果也不同,两者都不是等价关系,一直都很混乱.很抱歉让你陷入这样的混乱,但我到达时却是一团糟.

So, its a mess on all levels. == and Equals have different dispatch mechanisms and give different results, neither of them are equivalence relations, and it's all confusing all the time. Apologies for getting you into this mess, but it was a mess when I arrived.

关于为什么 C# 中的平等很糟糕的一个稍微不同的看法,请参阅我的令人遗憾的语言决定列表中的第 9 项,这里:http://www.informit.com/articles/article.aspx?p=2425867

For a slightly different take on why equality is terrible in C#, see item number nine on my list of regrettable language decisions, here: http://www.informit.com/articles/article.aspx?p=2425867

奖励练习:重复上面的分析,但是对于 x?.Equals(y) 对于 x 可以为空的情况.什么时候得到与不可为空的接收者相同的结果,什么时候得到不同的结果?

BONUS EXERCISE: Repeat the above analysis, but for x?.Equals(y) for the cases where x is nullable. When do you get the same results as for non-nullable receivers, and when do you get different results?

这篇关于double 和有什么不一样?和国际?对于 .Equals 比较?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆