Equals方法的奇怪行为

时间:2018-05-15 17:07:03

标签: c# .net

根据这个答案 https://stackoverflow.com/a/8315745/5324847 .Net应该通过反射或按位来比较值类型。但是,如果我们看一下这段代码,我们就看不到它们了(或者两者都没有)。

//Bitwise test
//if .Net compares bitwise then last line should be false
//if it uses reflections then last line should be true
float a = -0.0f;
float b = 0.0f;
Console.WriteLine(string.Join("",BitConverter.GetBytes(a))); //prints 000128
Console.WriteLine(string.Join("",BitConverter.GetBytes(b))); //prints 0000
Console.WriteLine(a == b); //True
Console.WriteLine(a.Equals(b)); //prints True


//Reflection test
//if .Net compares using reflection then last line should be false
//if it uses bitwise comparsion then last line should be true
a = float.NaN;
b = float.NaN;
Console.WriteLine(string.Join("",BitConverter.GetBytes(a))); //prints 00192255
Console.WriteLine(string.Join("",BitConverter.GetBytes(b))); //prints 00192255
Console.WriteLine(a == b); //False
Console.WriteLine(a.Equals(b)); //prints True

如果我们将变量包装在结构中.Net使用按位比较。 证明:

public struct FloatS
{
    public float x; 
}
public static void Main()
{
    //bitwise test
    //if .Net compares bitwise then last line should be false
    //if it uses reflections then last line should be true
    FloatS a = new FloatS();
    FloatS b = new FloatS();
    a.x = -0.0f;
    b.x = 0.0f;
    Console.WriteLine(string.Join("",BitConverter.GetBytes(a.x))); //prints 000128
    Console.WriteLine(string.Join("",BitConverter.GetBytes(b.x))); //prints 0000
    Console.WriteLine(a.x == b.x); //True
    Console.WriteLine(a.Equals(b)); //prints False -- this time corectly because it's bitwise comparsion


    //Reflection test
    //if .Net compares using reflection then last line should be false
    //if it uses bitwise comparsion then last line should be true
    a.x = float.NaN;
    b.x = float.NaN;
    Console.WriteLine(string.Join("",BitConverter.GetBytes(a.x))); //prints 00192255
    Console.WriteLine(string.Join("",BitConverter.GetBytes(b.x))); //prints 00192255
    Console.WriteLine(a.x == b.x); //False
    Console.WriteLine(a.Equals(b)); //prints True - also corectly
}

我的假设是.Net对原始类型使用不同的Equals实现。但这看起来很复杂,因为float和struct类型都可以转换为System.ValueType

有人可以解释发生了什么吗?

1 个答案:

答案 0 :(得分:4)

  

根据...... .NET应该通过反射或按位来比较值类型。

您误解了该答案,即用户定义的值类型

  • 对于==,使用浮动的特殊指令比较浮点数。零和负零相等,NaN等于零,甚至不等,等等。

  • 任何类型都可以覆盖EqualsSystem.Single可以覆盖x.Equals(y)。浮点数上的(x == y) || (IsNaN(x) && IsNaN(y))具有Equals的语义。我不确定为什么==Equals有不同的行为,但可能确保浮点数上的Equals是等价关系,这对于某些应用程序是必需的例如将浮动放入哈希表。 (更新:我的假设是正确的;请参阅下面的评论。)

如果您编写了用户定义的值类型而没有提供自己的Equals,那么您将获得默认值,正如您所说,它有两个大问题:

  • 有时效率不高
  • 包装浮点数的结构不会自动获得浮点相等行为。更一般地说,包装T的结构不会自动获得T等式行为。
  

我的假设是.NET对原始类型使用不同的Equals实现。但这似乎是不正确的,因为float和struct类型都可以转换为System.ValueType。

这一段对我来说没有任何意义,所以要么我误解了你,要么你相信继承的完全错误。每种类型都可以覆盖#include <iostream> #include <string> #include <utility> template<typename OneType> void foo_(OneType&& one) { std::cout << one; } template<typename... ArgTypes> void foo(ArgTypes&&... arguments) { (foo_(std::forward<ArgTypes>(arguments)), ...); } int main() { foo(42, 43., "Hello", std::string("Bla")); } 。这与用户代码可以包装任何非可空值类型的事实有什么关系?我没有看到你的两个句子之间的联系,而且你认为有一个句子的事实表明某人在这里有错误的信念。