double和有什么不一样?和诠释?等于比较?

时间:2018-09-26 18:03:53

标签: c# nullable

我不了解我的处境。下面是简化的情况:

list1 = [['a','b'],['c','d']]
[{"name": x[0], "value": x[1]} for x in list1]

我不明白为什么一个表达会使我成为事实,而另一个使我成为错误。它们看起来完全一样。

2 个答案:

答案 0 :(得分: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));

除了我标记为打印为假的那些以外,所有这些都打印为真。

我现在将对这些情况进行分析。

首先要注意的是==运算符总是说True。为什么会这样?

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

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,并且比较结果为true。

可为空的==的语义是:

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

同样,我们看到对于可空比较,int? == double?int? == double,依此类推,我们总是退回到非可空情况,将int?转换为{ {1}},并进行双精度比较。因此,这些也都是正确的。

现在我们来到double,这是混乱的地方。

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

让我们开始看看Equals的作用。如果对double.Equals(object)的呼叫的接收者为Equals(object),则如果参数不是黑框,则认为它们不相等。也就是说,double要求类型 match ,而Equals要求类型可以转换为通用类型

我再说一遍。与==不同,double.Equals不会 尝试将其参数转换为double。它只是检查是否已经的两倍,如果不是,则表示它们不相等。

然后说明了为什么==是错误的...但是...请稍等,上面的不是错误!是什么原因解释了?

d.Equals(i)已超载!上面我们实际上是在调用double.Equals,您猜对了,它会在进行调用之前将int转换为double!如果我们说过double.Equals(double),那将是错误的。

好的,所以我们知道为什么d.Equals((object)i))是正确的-因为int转换为double。

我们也知道为什么double.Equals(int)为假。 double.Equals(int?)不能转换为double,但是可以转换为int?。因此,我们称object并在double.Equals(object)框内输入,现在不相等。

int呢?的语义是:

  • 如果接收者为null且参数为null,则它们相等。
  • 如果接收者不为null,则遵循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无法转换为double,d无法转换为int。

这真是一团糟。我们希望平等是一个对等关系,事实并非如此!平等关系应具有以下属性:

  • 反身性:事物等于自身。尽管有一些例外情况,但在C#中通常是
  • 对称性:如果A等于B,则B等于A。这在C#中对==是正确的,但对A.Equals(B)而言并非如此。
  • 传递性:如果A等于B,B等于C,那么A也等于C。在C#中绝对不是这种情况。

因此,在所有级别上都是一团糟。 ==Equals具有不同的调度机制并给出不同的结果,它们都不是等价关系,并且始终令人困惑。抱歉让您陷入困境,但是当我到达时真是一团糟。

要对C#中的平等为何如此糟糕的原因有一些不同的看法,请参阅我的令人遗憾的语言决定列表中的第9个项目,这里:http://www.informit.com/articles/article.aspx?p=2425867

奖金练习:重复以上分析,但对于x?.Equals(y)可为空的情况,请重复x。什么时候获得与非空接收器相同的结果,什么时候获得不同的结果?

答案 1 :(得分:1)

每种类型的Equals方法的答案似乎都在答案中。如果类型不匹配,则它们不相等。

https://referencesource.microsoft.com/#mscorlib/system/double.cs,147

// True if obj is another Double with the same value as the current instance.  This is
// a method of object equality, that only returns true if obj is also a double.
public override bool Equals(Object obj) {
    if (!(obj is Double)) {
        return false;
    }
    double temp = ((Double)obj).m_value;
    // This code below is written this way for performance reasons i.e the != and == check is intentional.
    if (temp == m_value) {
        return true;
    }
    return IsNaN(temp) && IsNaN(m_value);
}

https://referencesource.microsoft.com/#mscorlib/system/int32.cs,72

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