具有函数case的并集类型上的F#等式行为
我试图理解这种平等行为。记录相等性测试失败,但记录唯一属性的相等性测试通过。这是虫子吗?或者有人能解释这种行为吗具有函数case的并集类型上的F#等式行为,f#,equality,discriminated-union,F#,Equality,Discriminated Union,我试图理解这种平等行为。记录相等性测试失败,但记录唯一属性的相等性测试通过。这是虫子吗?或者有人能解释这种行为吗 type TestUnion = | Case1 | Case2 of (int -> string) type TestType = { Foo : TestUnion } open Microsoft.VisualStudio.TestTools.UnitTesting [<TestClass>] publi
type TestUnion =
| Case1
| Case2 of (int -> string)
type TestType =
{
Foo : TestUnion
}
open Microsoft.VisualStudio.TestTools.UnitTesting
[<TestClass>]
public Testing() =
let a = { Foo = Case1 }
let b = { Foo = Case1 }
[<TestMethod>]
member __.ThisFails () =
Assert.AreEqual(a, b)
[<TestMethod>]
member __.ThisPasses () =
Assert.AreEqual(a.Foo, b.Foo)
类型TestUnion=
|案例1
|案例2(整数->字符串)
类型TestType=
{
Foo:TestUnion
}
打开Microsoft.VisualStudio.TestTools.UnitTesting
[]
公共测试()=
设a={Foo=Case1}
设b={Foo=Case1}
[]
成员“”失败()=
断言.AreEqual(a,b)
[]
成员_uuu.ThisPasses()=
Assert.AreEqual(a.Foo,b.Foo)
我知道它失败的原因是因为其中一个例子是函数。如果我将其更改为简单值,则两个测试都通过。但我感到奇怪的是,a)等式根本失败,因为使用了没有值的简单情况;b)记录等式失败,而属性等式通过
注意:如果还存在其他简单属性,则记录相等性将失败。这样,即使union类型属性测试为相等,union类型也会破坏整个记录的相等性。如果您尝试直接比较
a=b
,则应该会看到此错误:
错误FS0001:类型“TestType”不支持“相等”约束,因为它是一个记录、联合或结构,具有一个或多个不支持“相等”约束的结构元素类型。避免在此类型中使用相等,或向该类型添加“StructuralEquality”属性以确定哪个字段类型不支持相等
函数值不支持F#中的相等,因此在这样的类型上不能有结构相等。我怀疑Assert.AreEqual
调用不依赖于F#的结构相等比较
let areEqual x y = obj.Equals(x, y)
> areEqual a.Foo b.Foo;;
val it : bool = true
Assert.AreEqual
方法试图变得聪明,当然失败了。给定两个对象,此方法要做的第一件事是测试引用相等性:obj.ReferenceEquals(Case1,Case1)
。这会立即起作用,因为所有Case1
值都是相同的对象
现在,如果Assert.AreEqual
的参数不是同一个对象,它将继续调用obj.Equals
。根据您的记录,Equals
的实现将始终返回false,因为F#编译器没有为其实现相等。为什么?因为某些字段的类型(即,TestUnion
)不相等。为什么TestUnion
没有相等性?因为它至少有一个大小写的类型不相等,即,int->string
如果将Case1
更改为int的Case1,然后尝试Assert.AreEqual(Case1 42,Case1 42)
,测试将失败。这将发生,因为Case1 42
的两个实例化将不再是同一个对象(除非使用优化编译),并且TestUnion的Equals
实现将始终返回false
如果您真的希望这样做(并且您真的知道如何比较函数),您可以自己实现Equals
:
[<CustomEquality; NoComparison>]
type TestType = { Foo: TestUnion }
with
override this.Equals other = (* whatever *)
override this.GetHashCode() = (* whatever *)
这样,如果您缺少某些内容,编译器将始终告诉您:
Assert.IsTrue( a = b )
// The type 'TestType' does not support the 'equality' constraint because blah-blah-blah
F#编译器通常比底层的.NET CLR更正确、更一致。编辑前面的注释。我玩的时候换了一种。你说得对。当我直接做等式比较时,我得到了错误。很好的解释。我确实尝试了Assert.AreEqual(case142,case142)
,这确实没有让我感到惊讶。我认为我已经很聪明了,我自己制作了areEqual
函数,它需要相同的类型(但仍然称为Assert.areEqual
)。这对于编译器来说仍然过于间接,无法给出错误。
Assert.IsTrue( a = b )
// The type 'TestType' does not support the 'equality' constraint because blah-blah-blah